| GCC Code Coverage Report | |||||||||||||||||||||
|
|||||||||||||||||||||
| Line | Branch | Exec | Source |
1 |
#include "mjs.h" |
||
2 |
#ifdef MJS_MODULE_LINES |
||
3 |
#line 1 "common/platform.h" |
||
4 |
#endif |
||
5 |
#ifndef CS_COMMON_PLATFORM_H_ |
||
6 |
#define CS_COMMON_PLATFORM_H_ |
||
7 |
|||
8 |
/* |
||
9 |
* For the "custom" platform, includes and dependencies can be |
||
10 |
* provided through mg_locals.h. |
||
11 |
*/ |
||
12 |
#define CS_P_CUSTOM 0 |
||
13 |
#define CS_P_UNIX 1 |
||
14 |
#define CS_P_WINDOWS 2 |
||
15 |
#define CS_P_ESP32 15 |
||
16 |
#define CS_P_ESP8266 3 |
||
17 |
#define CS_P_CC3100 6 |
||
18 |
#define CS_P_CC3200 4 |
||
19 |
#define CS_P_CC3220 17 |
||
20 |
#define CS_P_MSP432 5 |
||
21 |
#define CS_P_TM4C129 14 |
||
22 |
#define CS_P_MBED 7 |
||
23 |
#define CS_P_WINCE 8 |
||
24 |
#define CS_P_NXP_LPC 13 |
||
25 |
#define CS_P_NXP_KINETIS 9 |
||
26 |
#define CS_P_NRF51 12 |
||
27 |
#define CS_P_NRF52 10 |
||
28 |
#define CS_P_PIC32 11 |
||
29 |
#define CS_P_STM32 16 |
||
30 |
/* Next id: 18 */ |
||
31 |
|||
32 |
/* If not specified explicitly, we guess platform by defines. */ |
||
33 |
#ifndef CS_PLATFORM |
||
34 |
|||
35 |
#if defined(TARGET_IS_MSP432P4XX) || defined(__MSP432P401R__) |
||
36 |
#define CS_PLATFORM CS_P_MSP432 |
||
37 |
#elif defined(cc3200) || defined(TARGET_IS_CC3200) |
||
38 |
#define CS_PLATFORM CS_P_CC3200 |
||
39 |
#elif defined(cc3220) || defined(TARGET_IS_CC3220) |
||
40 |
#define CS_PLATFORM CS_P_CC3220 |
||
41 |
#elif defined(__unix__) || defined(__APPLE__) |
||
42 |
#define CS_PLATFORM CS_P_UNIX |
||
43 |
#elif defined(WINCE) |
||
44 |
#define CS_PLATFORM CS_P_WINCE |
||
45 |
#elif defined(_WIN32) |
||
46 |
#define CS_PLATFORM CS_P_WINDOWS |
||
47 |
#elif defined(__MBED__) |
||
48 |
#define CS_PLATFORM CS_P_MBED |
||
49 |
#elif defined(__USE_LPCOPEN) |
||
50 |
#define CS_PLATFORM CS_P_NXP_LPC |
||
51 |
#elif defined(FRDM_K64F) || defined(FREEDOM) |
||
52 |
#define CS_PLATFORM CS_P_NXP_KINETIS |
||
53 |
#elif defined(PIC32) |
||
54 |
#define CS_PLATFORM CS_P_PIC32 |
||
55 |
#elif defined(ESP_PLATFORM) |
||
56 |
#define CS_PLATFORM CS_P_ESP32 |
||
57 |
#elif defined(ICACHE_FLASH) |
||
58 |
#define CS_PLATFORM CS_P_ESP8266 |
||
59 |
#elif defined(TARGET_IS_TM4C129_RA0) || defined(TARGET_IS_TM4C129_RA1) || \ |
||
60 |
defined(TARGET_IS_TM4C129_RA2) |
||
61 |
#define CS_PLATFORM CS_P_TM4C129 |
||
62 |
#elif defined(STM32) |
||
63 |
#define CS_PLATFORM CS_P_STM32 |
||
64 |
#endif |
||
65 |
|||
66 |
#ifndef CS_PLATFORM |
||
67 |
#error "CS_PLATFORM is not specified and we couldn't guess it." |
||
68 |
#endif |
||
69 |
|||
70 |
#endif /* !defined(CS_PLATFORM) */ |
||
71 |
|||
72 |
#define MG_NET_IF_SOCKET 1 |
||
73 |
#define MG_NET_IF_SIMPLELINK 2 |
||
74 |
#define MG_NET_IF_LWIP_LOW_LEVEL 3 |
||
75 |
#define MG_NET_IF_PIC32 4 |
||
76 |
|||
77 |
#define MG_SSL_IF_OPENSSL 1 |
||
78 |
#define MG_SSL_IF_MBEDTLS 2 |
||
79 |
#define MG_SSL_IF_SIMPLELINK 3 |
||
80 |
|||
81 |
/* Amalgamated: #include "common/platforms/platform_unix.h" */ |
||
82 |
/* Amalgamated: #include "common/platforms/platform_windows.h" */ |
||
83 |
/* Amalgamated: #include "common/platforms/platform_esp32.h" */ |
||
84 |
/* Amalgamated: #include "common/platforms/platform_esp8266.h" */ |
||
85 |
/* Amalgamated: #include "common/platforms/platform_cc3100.h" */ |
||
86 |
/* Amalgamated: #include "common/platforms/platform_cc3200.h" */ |
||
87 |
/* Amalgamated: #include "common/platforms/platform_cc3220.h" */ |
||
88 |
/* Amalgamated: #include "common/platforms/platform_mbed.h" */ |
||
89 |
/* Amalgamated: #include "common/platforms/platform_nrf51.h" */ |
||
90 |
/* Amalgamated: #include "common/platforms/platform_nrf52.h" */ |
||
91 |
/* Amalgamated: #include "common/platforms/platform_wince.h" */ |
||
92 |
/* Amalgamated: #include "common/platforms/platform_nxp_lpc.h" */ |
||
93 |
/* Amalgamated: #include "common/platforms/platform_nxp_kinetis.h" */ |
||
94 |
/* Amalgamated: #include "common/platforms/platform_pic32.h" */ |
||
95 |
/* Amalgamated: #include "common/platforms/platform_stm32.h" */ |
||
96 |
|||
97 |
/* Common stuff */ |
||
98 |
|||
99 |
#if !defined(PRINTF_LIKE) |
||
100 |
#if defined(__GNUC__) || defined(__clang__) || defined(__TI_COMPILER_VERSION__) |
||
101 |
#define PRINTF_LIKE(f, a) __attribute__((format(printf, f, a))) |
||
102 |
#else |
||
103 |
#define PRINTF_LIKE(f, a) |
||
104 |
#endif |
||
105 |
#endif |
||
106 |
|||
107 |
#if !defined(WEAK) |
||
108 |
#if (defined(__GNUC__) || defined(__clang__) || \ |
||
109 |
defined(__TI_COMPILER_VERSION__)) && \ |
||
110 |
!defined(_WIN32) |
||
111 |
#define WEAK __attribute__((weak)) |
||
112 |
#else |
||
113 |
#define WEAK |
||
114 |
#endif |
||
115 |
#endif |
||
116 |
|||
117 |
#ifdef __GNUC__ |
||
118 |
#define NORETURN __attribute__((noreturn)) |
||
119 |
#define NOINLINE __attribute__((noinline)) |
||
120 |
#define WARN_UNUSED_RESULT __attribute__((warn_unused_result)) |
||
121 |
#define NOINSTR __attribute__((no_instrument_function)) |
||
122 |
#define DO_NOT_WARN_UNUSED __attribute__((unused)) |
||
123 |
#else |
||
124 |
#define NORETURN |
||
125 |
#define NOINLINE |
||
126 |
#define WARN_UNUSED_RESULT |
||
127 |
#define NOINSTR |
||
128 |
#define DO_NOT_WARN_UNUSED |
||
129 |
#endif /* __GNUC__ */ |
||
130 |
|||
131 |
#ifndef ARRAY_SIZE |
||
132 |
#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0])) |
||
133 |
#endif |
||
134 |
|||
135 |
#endif /* CS_COMMON_PLATFORM_H_ */ |
||
136 |
#ifdef MJS_MODULE_LINES |
||
137 |
#line 1 "common/platforms/platform_windows.h" |
||
138 |
#endif |
||
139 |
#ifndef CS_COMMON_PLATFORMS_PLATFORM_WINDOWS_H_ |
||
140 |
#define CS_COMMON_PLATFORMS_PLATFORM_WINDOWS_H_ |
||
141 |
#if CS_PLATFORM == CS_P_WINDOWS |
||
142 |
|||
143 |
/* |
||
144 |
* MSVC++ 14.0 _MSC_VER == 1900 (Visual Studio 2015) |
||
145 |
* MSVC++ 12.0 _MSC_VER == 1800 (Visual Studio 2013) |
||
146 |
* MSVC++ 11.0 _MSC_VER == 1700 (Visual Studio 2012) |
||
147 |
* MSVC++ 10.0 _MSC_VER == 1600 (Visual Studio 2010) |
||
148 |
* MSVC++ 9.0 _MSC_VER == 1500 (Visual Studio 2008) |
||
149 |
* MSVC++ 8.0 _MSC_VER == 1400 (Visual Studio 2005) |
||
150 |
* MSVC++ 7.1 _MSC_VER == 1310 (Visual Studio 2003) |
||
151 |
* MSVC++ 7.0 _MSC_VER == 1300 |
||
152 |
* MSVC++ 6.0 _MSC_VER == 1200 |
||
153 |
* MSVC++ 5.0 _MSC_VER == 1100 |
||
154 |
*/ |
||
155 |
#ifdef _MSC_VER |
||
156 |
#pragma warning(disable : 4127) /* FD_SET() emits warning, disable it */ |
||
157 |
#pragma warning(disable : 4204) /* missing c99 support */ |
||
158 |
#endif |
||
159 |
|||
160 |
#ifndef _WINSOCK_DEPRECATED_NO_WARNINGS |
||
161 |
#define _WINSOCK_DEPRECATED_NO_WARNINGS 1 |
||
162 |
#endif |
||
163 |
|||
164 |
#ifndef _CRT_SECURE_NO_WARNINGS |
||
165 |
#define _CRT_SECURE_NO_WARNINGS |
||
166 |
#endif |
||
167 |
|||
168 |
#include <assert.h> |
||
169 |
#include <direct.h> |
||
170 |
#include <errno.h> |
||
171 |
#include <fcntl.h> |
||
172 |
#include <io.h> |
||
173 |
#include <limits.h> |
||
174 |
#include <signal.h> |
||
175 |
#include <stddef.h> |
||
176 |
#include <stdio.h> |
||
177 |
#include <stdlib.h> |
||
178 |
#include <sys/stat.h> |
||
179 |
#include <time.h> |
||
180 |
#include <ctype.h> |
||
181 |
|||
182 |
#ifdef _MSC_VER |
||
183 |
#pragma comment(lib, "ws2_32.lib") /* Linking with winsock library */ |
||
184 |
#endif |
||
185 |
|||
186 |
#include <winsock2.h> |
||
187 |
#include <ws2tcpip.h> |
||
188 |
#include <windows.h> |
||
189 |
#include <process.h> |
||
190 |
|||
191 |
#if _MSC_VER < 1700 |
||
192 |
typedef int bool; |
||
193 |
#else |
||
194 |
#include <stdbool.h> |
||
195 |
#endif |
||
196 |
|||
197 |
#if defined(_MSC_VER) && _MSC_VER >= 1800 |
||
198 |
#define strdup _strdup |
||
199 |
#endif |
||
200 |
|||
201 |
#ifndef EINPROGRESS |
||
202 |
#define EINPROGRESS WSAEINPROGRESS |
||
203 |
#endif |
||
204 |
#ifndef EWOULDBLOCK |
||
205 |
#define EWOULDBLOCK WSAEWOULDBLOCK |
||
206 |
#endif |
||
207 |
#ifndef __func__ |
||
208 |
#define STRX(x) #x |
||
209 |
#define STR(x) STRX(x) |
||
210 |
#define __func__ __FILE__ ":" STR(__LINE__) |
||
211 |
#endif |
||
212 |
#define snprintf _snprintf |
||
213 |
#define vsnprintf _vsnprintf |
||
214 |
#define to64(x) _atoi64(x) |
||
215 |
#if !defined(__MINGW32__) && !defined(__MINGW64__) |
||
216 |
#define popen(x, y) _popen((x), (y)) |
||
217 |
#define pclose(x) _pclose(x) |
||
218 |
#define fileno _fileno |
||
219 |
#endif |
||
220 |
#if defined(_MSC_VER) && _MSC_VER >= 1400 |
||
221 |
#define fseeko(x, y, z) _fseeki64((x), (y), (z)) |
||
222 |
#else |
||
223 |
#define fseeko(x, y, z) fseek((x), (y), (z)) |
||
224 |
#endif |
||
225 |
#if defined(_MSC_VER) && _MSC_VER <= 1200 |
||
226 |
typedef unsigned long uintptr_t; |
||
227 |
typedef long intptr_t; |
||
228 |
#endif |
||
229 |
typedef int socklen_t; |
||
230 |
#if _MSC_VER >= 1700 |
||
231 |
#include <stdint.h> |
||
232 |
#else |
||
233 |
typedef signed char int8_t; |
||
234 |
typedef unsigned char uint8_t; |
||
235 |
typedef int int32_t; |
||
236 |
typedef unsigned int uint32_t; |
||
237 |
typedef short int16_t; |
||
238 |
typedef unsigned short uint16_t; |
||
239 |
typedef __int64 int64_t; |
||
240 |
typedef unsigned __int64 uint64_t; |
||
241 |
#endif |
||
242 |
typedef SOCKET sock_t; |
||
243 |
typedef uint32_t in_addr_t; |
||
244 |
#ifndef UINT16_MAX |
||
245 |
#define UINT16_MAX 65535 |
||
246 |
#endif |
||
247 |
#ifndef UINT32_MAX |
||
248 |
#define UINT32_MAX 4294967295 |
||
249 |
#endif |
||
250 |
#ifndef pid_t |
||
251 |
#define pid_t HANDLE |
||
252 |
#endif |
||
253 |
#define INT64_FMT "I64d" |
||
254 |
#define INT64_X_FMT "I64x" |
||
255 |
#define SIZE_T_FMT "Iu" |
||
256 |
typedef struct _stati64 cs_stat_t; |
||
257 |
#ifndef S_ISDIR |
||
258 |
#define S_ISDIR(x) (((x) &_S_IFMT) == _S_IFDIR) |
||
259 |
#endif |
||
260 |
#ifndef S_ISREG |
||
261 |
#define S_ISREG(x) (((x) &_S_IFMT) == _S_IFREG) |
||
262 |
#endif |
||
263 |
#define DIRSEP '\\' |
||
264 |
#define CS_DEFINE_DIRENT |
||
265 |
|||
266 |
#ifndef va_copy |
||
267 |
#ifdef __va_copy |
||
268 |
#define va_copy __va_copy |
||
269 |
#else |
||
270 |
#define va_copy(x, y) (x) = (y) |
||
271 |
#endif |
||
272 |
#endif |
||
273 |
|||
274 |
#ifndef MG_MAX_HTTP_REQUEST_SIZE |
||
275 |
#define MG_MAX_HTTP_REQUEST_SIZE 8192 |
||
276 |
#endif |
||
277 |
|||
278 |
#ifndef MG_MAX_HTTP_SEND_MBUF |
||
279 |
#define MG_MAX_HTTP_SEND_MBUF 4096 |
||
280 |
#endif |
||
281 |
|||
282 |
#ifndef MG_MAX_HTTP_HEADERS |
||
283 |
#define MG_MAX_HTTP_HEADERS 40 |
||
284 |
#endif |
||
285 |
|||
286 |
#ifndef CS_ENABLE_STDIO |
||
287 |
#define CS_ENABLE_STDIO 1 |
||
288 |
#endif |
||
289 |
|||
290 |
#ifndef MG_ENABLE_BROADCAST |
||
291 |
#define MG_ENABLE_BROADCAST 1 |
||
292 |
#endif |
||
293 |
|||
294 |
#ifndef MG_ENABLE_DIRECTORY_LISTING |
||
295 |
#define MG_ENABLE_DIRECTORY_LISTING 1 |
||
296 |
#endif |
||
297 |
|||
298 |
#ifndef MG_ENABLE_FILESYSTEM |
||
299 |
#define MG_ENABLE_FILESYSTEM 1 |
||
300 |
#endif |
||
301 |
|||
302 |
#ifndef MG_ENABLE_HTTP_CGI |
||
303 |
#define MG_ENABLE_HTTP_CGI MG_ENABLE_FILESYSTEM |
||
304 |
#endif |
||
305 |
|||
306 |
#ifndef MG_NET_IF |
||
307 |
#define MG_NET_IF MG_NET_IF_SOCKET |
||
308 |
#endif |
||
309 |
|||
310 |
unsigned int sleep(unsigned int seconds); |
||
311 |
|||
312 |
/* https://stackoverflow.com/questions/16647819/timegm-cross-platform */ |
||
313 |
#define timegm _mkgmtime |
||
314 |
|||
315 |
#define gmtime_r(a, b) \ |
||
316 |
do { \ |
||
317 |
*(b) = *gmtime(a); \ |
||
318 |
} while (0) |
||
319 |
|||
320 |
#endif /* CS_PLATFORM == CS_P_WINDOWS */ |
||
321 |
#endif /* CS_COMMON_PLATFORMS_PLATFORM_WINDOWS_H_ */ |
||
322 |
#ifdef MJS_MODULE_LINES |
||
323 |
#line 1 "common/platforms/platform_unix.h" |
||
324 |
#endif |
||
325 |
#ifndef CS_COMMON_PLATFORMS_PLATFORM_UNIX_H_ |
||
326 |
#define CS_COMMON_PLATFORMS_PLATFORM_UNIX_H_ |
||
327 |
#if CS_PLATFORM == CS_P_UNIX |
||
328 |
|||
329 |
#ifndef _XOPEN_SOURCE |
||
330 |
#define _XOPEN_SOURCE 600 |
||
331 |
#endif |
||
332 |
|||
333 |
/* <inttypes.h> wants this for C++ */ |
||
334 |
#ifndef __STDC_FORMAT_MACROS |
||
335 |
#define __STDC_FORMAT_MACROS |
||
336 |
#endif |
||
337 |
|||
338 |
/* C++ wants that for INT64_MAX */ |
||
339 |
#ifndef __STDC_LIMIT_MACROS |
||
340 |
#define __STDC_LIMIT_MACROS |
||
341 |
#endif |
||
342 |
|||
343 |
/* Enable fseeko() and ftello() functions */ |
||
344 |
#ifndef _LARGEFILE_SOURCE |
||
345 |
#define _LARGEFILE_SOURCE |
||
346 |
#endif |
||
347 |
|||
348 |
/* Enable 64-bit file offsets */ |
||
349 |
#ifndef _FILE_OFFSET_BITS |
||
350 |
#define _FILE_OFFSET_BITS 64 |
||
351 |
#endif |
||
352 |
|||
353 |
#include <arpa/inet.h> |
||
354 |
#include <assert.h> |
||
355 |
#include <ctype.h> |
||
356 |
#include <dirent.h> |
||
357 |
#include <errno.h> |
||
358 |
#include <fcntl.h> |
||
359 |
#include <inttypes.h> |
||
360 |
#include <stdint.h> |
||
361 |
#include <limits.h> |
||
362 |
#include <math.h> |
||
363 |
#include <netdb.h> |
||
364 |
#include <netinet/in.h> |
||
365 |
#include <pthread.h> |
||
366 |
#include <signal.h> |
||
367 |
#include <stdarg.h> |
||
368 |
#include <stdbool.h> |
||
369 |
#include <stdio.h> |
||
370 |
#include <stdlib.h> |
||
371 |
#include <string.h> |
||
372 |
#include <sys/param.h> |
||
373 |
#include <sys/socket.h> |
||
374 |
#include <sys/select.h> |
||
375 |
#include <sys/stat.h> |
||
376 |
#include <sys/time.h> |
||
377 |
#include <sys/types.h> |
||
378 |
#include <unistd.h> |
||
379 |
|||
380 |
#ifdef __APPLE__ |
||
381 |
#include <machine/endian.h> |
||
382 |
#ifndef BYTE_ORDER |
||
383 |
#define LITTLE_ENDIAN __DARWIN_LITTLE_ENDIAN |
||
384 |
#define BIG_ENDIAN __DARWIN_BIG_ENDIAN |
||
385 |
#define PDP_ENDIAN __DARWIN_PDP_ENDIAN |
||
386 |
#define BYTE_ORDER __DARWIN_BYTE_ORDER |
||
387 |
#endif |
||
388 |
#endif |
||
389 |
|||
390 |
/* |
||
391 |
* osx correctly avoids defining strtoll when compiling in strict ansi mode. |
||
392 |
* c++ 11 standard defines strtoll as well. |
||
393 |
* We require strtoll, and if your embedded pre-c99 compiler lacks one, please |
||
394 |
* implement a shim. |
||
395 |
*/ |
||
396 |
#if !(defined(__cplusplus) && __cplusplus >= 201103L) && \ |
||
397 |
!(defined(__DARWIN_C_LEVEL) && __DARWIN_C_LEVEL >= 200809L) |
||
398 |
long long strtoll(const char *, char **, int); |
||
399 |
#endif |
||
400 |
|||
401 |
typedef int sock_t; |
||
402 |
#define INVALID_SOCKET (-1) |
||
403 |
#define SIZE_T_FMT "zu" |
||
404 |
typedef struct stat cs_stat_t; |
||
405 |
#define DIRSEP '/' |
||
406 |
#define to64(x) strtoll(x, NULL, 10) |
||
407 |
#define INT64_FMT PRId64 |
||
408 |
#define INT64_X_FMT PRIx64 |
||
409 |
|||
410 |
#ifndef __cdecl |
||
411 |
#define __cdecl |
||
412 |
#endif |
||
413 |
|||
414 |
#ifndef va_copy |
||
415 |
#ifdef __va_copy |
||
416 |
#define va_copy __va_copy |
||
417 |
#else |
||
418 |
#define va_copy(x, y) (x) = (y) |
||
419 |
#endif |
||
420 |
#endif |
||
421 |
|||
422 |
#define closesocket(x) close(x) |
||
423 |
|||
424 |
#ifndef MG_MAX_HTTP_REQUEST_SIZE |
||
425 |
#define MG_MAX_HTTP_REQUEST_SIZE 8192 |
||
426 |
#endif |
||
427 |
|||
428 |
#ifndef MG_MAX_HTTP_SEND_MBUF |
||
429 |
#define MG_MAX_HTTP_SEND_MBUF 4096 |
||
430 |
#endif |
||
431 |
|||
432 |
#ifndef MG_MAX_HTTP_HEADERS |
||
433 |
#define MG_MAX_HTTP_HEADERS 40 |
||
434 |
#endif |
||
435 |
|||
436 |
#ifndef CS_ENABLE_STDIO |
||
437 |
#define CS_ENABLE_STDIO 1 |
||
438 |
#endif |
||
439 |
|||
440 |
#ifndef MG_ENABLE_BROADCAST |
||
441 |
#define MG_ENABLE_BROADCAST 1 |
||
442 |
#endif |
||
443 |
|||
444 |
#ifndef MG_ENABLE_DIRECTORY_LISTING |
||
445 |
#define MG_ENABLE_DIRECTORY_LISTING 1 |
||
446 |
#endif |
||
447 |
|||
448 |
#ifndef MG_ENABLE_FILESYSTEM |
||
449 |
#define MG_ENABLE_FILESYSTEM 1 |
||
450 |
#endif |
||
451 |
|||
452 |
#ifndef MG_ENABLE_HTTP_CGI |
||
453 |
#define MG_ENABLE_HTTP_CGI MG_ENABLE_FILESYSTEM |
||
454 |
#endif |
||
455 |
|||
456 |
#ifndef MG_NET_IF |
||
457 |
#define MG_NET_IF MG_NET_IF_SOCKET |
||
458 |
#endif |
||
459 |
|||
460 |
#ifndef MG_HOSTS_FILE_NAME |
||
461 |
#define MG_HOSTS_FILE_NAME "/etc/hosts" |
||
462 |
#endif |
||
463 |
|||
464 |
#ifndef MG_RESOLV_CONF_FILE_NAME |
||
465 |
#define MG_RESOLV_CONF_FILE_NAME "/etc/resolv.conf" |
||
466 |
#endif |
||
467 |
|||
468 |
#endif /* CS_PLATFORM == CS_P_UNIX */ |
||
469 |
#endif /* CS_COMMON_PLATFORMS_PLATFORM_UNIX_H_ */ |
||
470 |
#ifdef MJS_MODULE_LINES |
||
471 |
#line 1 "common/platforms/platform_esp32.h" |
||
472 |
#endif |
||
473 |
/* |
||
474 |
* Copyright (c) 2014-2018 Cesanta Software Limited |
||
475 |
* All rights reserved |
||
476 |
* |
||
477 |
* Licensed under the Apache License, Version 2.0 (the ""License""); |
||
478 |
* you may not use this file except in compliance with the License. |
||
479 |
* You may obtain a copy of the License at |
||
480 |
* |
||
481 |
* http://www.apache.org/licenses/LICENSE-2.0 |
||
482 |
* |
||
483 |
* Unless required by applicable law or agreed to in writing, software |
||
484 |
* distributed under the License is distributed on an ""AS IS"" BASIS, |
||
485 |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||
486 |
* See the License for the specific language governing permissions and |
||
487 |
* limitations under the License. |
||
488 |
*/ |
||
489 |
|||
490 |
#ifndef CS_COMMON_PLATFORMS_PLATFORM_ESP32_H_ |
||
491 |
#define CS_COMMON_PLATFORMS_PLATFORM_ESP32_H_ |
||
492 |
#if CS_PLATFORM == CS_P_ESP32 |
||
493 |
|||
494 |
#include <assert.h> |
||
495 |
#include <ctype.h> |
||
496 |
#include <dirent.h> |
||
497 |
#include <fcntl.h> |
||
498 |
#include <inttypes.h> |
||
499 |
#include <machine/endian.h> |
||
500 |
#include <stdbool.h> |
||
501 |
#include <stdint.h> |
||
502 |
#include <string.h> |
||
503 |
#include <sys/stat.h> |
||
504 |
#include <sys/time.h> |
||
505 |
|||
506 |
#define SIZE_T_FMT "u" |
||
507 |
typedef struct stat cs_stat_t; |
||
508 |
#define DIRSEP '/' |
||
509 |
#define to64(x) strtoll(x, NULL, 10) |
||
510 |
#define INT64_FMT PRId64 |
||
511 |
#define INT64_X_FMT PRIx64 |
||
512 |
#define __cdecl |
||
513 |
#define _FILE_OFFSET_BITS 32 |
||
514 |
|||
515 |
#define MG_LWIP 1 |
||
516 |
|||
517 |
#ifndef MG_NET_IF |
||
518 |
#define MG_NET_IF MG_NET_IF_SOCKET |
||
519 |
#endif |
||
520 |
|||
521 |
#ifndef CS_ENABLE_STDIO |
||
522 |
#define CS_ENABLE_STDIO 1 |
||
523 |
#endif |
||
524 |
|||
525 |
#endif /* CS_PLATFORM == CS_P_ESP32 */ |
||
526 |
#endif /* CS_COMMON_PLATFORMS_PLATFORM_ESP32_H_ */ |
||
527 |
#ifdef MJS_MODULE_LINES |
||
528 |
#line 1 "common/platforms/platform_esp8266.h" |
||
529 |
#endif |
||
530 |
/* |
||
531 |
* Copyright (c) 2014-2018 Cesanta Software Limited |
||
532 |
* All rights reserved |
||
533 |
* |
||
534 |
* Licensed under the Apache License, Version 2.0 (the ""License""); |
||
535 |
* you may not use this file except in compliance with the License. |
||
536 |
* You may obtain a copy of the License at |
||
537 |
* |
||
538 |
* http://www.apache.org/licenses/LICENSE-2.0 |
||
539 |
* |
||
540 |
* Unless required by applicable law or agreed to in writing, software |
||
541 |
* distributed under the License is distributed on an ""AS IS"" BASIS, |
||
542 |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||
543 |
* See the License for the specific language governing permissions and |
||
544 |
* limitations under the License. |
||
545 |
*/ |
||
546 |
|||
547 |
#ifndef CS_COMMON_PLATFORMS_PLATFORM_ESP8266_H_ |
||
548 |
#define CS_COMMON_PLATFORMS_PLATFORM_ESP8266_H_ |
||
549 |
#if CS_PLATFORM == CS_P_ESP8266 |
||
550 |
|||
551 |
#include <assert.h> |
||
552 |
#include <ctype.h> |
||
553 |
#include <fcntl.h> |
||
554 |
#include <inttypes.h> |
||
555 |
#include <machine/endian.h> |
||
556 |
#include <stdbool.h> |
||
557 |
#include <string.h> |
||
558 |
#include <sys/stat.h> |
||
559 |
#include <sys/time.h> |
||
560 |
|||
561 |
#define SIZE_T_FMT "u" |
||
562 |
typedef struct stat cs_stat_t; |
||
563 |
#define DIRSEP '/' |
||
564 |
#if !defined(MGOS_VFS_DEFINE_DIRENT) |
||
565 |
#define CS_DEFINE_DIRENT |
||
566 |
#endif |
||
567 |
|||
568 |
#define to64(x) strtoll(x, NULL, 10) |
||
569 |
#define INT64_FMT PRId64 |
||
570 |
#define INT64_X_FMT PRIx64 |
||
571 |
#define __cdecl |
||
572 |
#define _FILE_OFFSET_BITS 32 |
||
573 |
|||
574 |
#define MG_LWIP 1 |
||
575 |
|||
576 |
/* struct timeval is defined in sys/time.h. */ |
||
577 |
#define LWIP_TIMEVAL_PRIVATE 0 |
||
578 |
|||
579 |
#ifndef MG_NET_IF |
||
580 |
#include <lwip/opt.h> |
||
581 |
#if LWIP_SOCKET /* RTOS SDK has LWIP sockets */ |
||
582 |
#define MG_NET_IF MG_NET_IF_SOCKET |
||
583 |
#else |
||
584 |
#define MG_NET_IF MG_NET_IF_LWIP_LOW_LEVEL |
||
585 |
#endif |
||
586 |
#endif |
||
587 |
|||
588 |
#ifndef CS_ENABLE_STDIO |
||
589 |
#define CS_ENABLE_STDIO 1 |
||
590 |
#endif |
||
591 |
|||
592 |
#define inet_ntop(af, src, dst, size) \ |
||
593 |
(((af) == AF_INET) ? ipaddr_ntoa_r((const ip_addr_t *) (src), (dst), (size)) \ |
||
594 |
: NULL) |
||
595 |
#define inet_pton(af, src, dst) \ |
||
596 |
(((af) == AF_INET) ? ipaddr_aton((src), (ip_addr_t *) (dst)) : 0) |
||
597 |
|||
598 |
#endif /* CS_PLATFORM == CS_P_ESP8266 */ |
||
599 |
#endif /* CS_COMMON_PLATFORMS_PLATFORM_ESP8266_H_ */ |
||
600 |
#ifdef MJS_MODULE_LINES |
||
601 |
#line 1 "common/platforms/platform_cc3100.h" |
||
602 |
#endif |
||
603 |
/* |
||
604 |
* Copyright (c) 2014-2018 Cesanta Software Limited |
||
605 |
* All rights reserved |
||
606 |
* |
||
607 |
* Licensed under the Apache License, Version 2.0 (the ""License""); |
||
608 |
* you may not use this file except in compliance with the License. |
||
609 |
* You may obtain a copy of the License at |
||
610 |
* |
||
611 |
* http://www.apache.org/licenses/LICENSE-2.0 |
||
612 |
* |
||
613 |
* Unless required by applicable law or agreed to in writing, software |
||
614 |
* distributed under the License is distributed on an ""AS IS"" BASIS, |
||
615 |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||
616 |
* See the License for the specific language governing permissions and |
||
617 |
* limitations under the License. |
||
618 |
*/ |
||
619 |
|||
620 |
#ifndef CS_COMMON_PLATFORMS_PLATFORM_CC3100_H_ |
||
621 |
#define CS_COMMON_PLATFORMS_PLATFORM_CC3100_H_ |
||
622 |
#if CS_PLATFORM == CS_P_CC3100 |
||
623 |
|||
624 |
#include <assert.h> |
||
625 |
#include <ctype.h> |
||
626 |
#include <errno.h> |
||
627 |
#include <inttypes.h> |
||
628 |
#include <stdint.h> |
||
629 |
#include <string.h> |
||
630 |
#include <time.h> |
||
631 |
|||
632 |
#define MG_NET_IF MG_NET_IF_SIMPLELINK |
||
633 |
#define MG_SSL_IF MG_SSL_IF_SIMPLELINK |
||
634 |
|||
635 |
/* |
||
636 |
* CC3100 SDK and STM32 SDK include headers w/out path, just like |
||
637 |
* #include "simplelink.h". As result, we have to add all required directories |
||
638 |
* into Makefile IPATH and do the same thing (include w/out path) |
||
639 |
*/ |
||
640 |
|||
641 |
#include <simplelink.h> |
||
642 |
#include <netapp.h> |
||
643 |
#undef timeval |
||
644 |
|||
645 |
typedef int sock_t; |
||
646 |
#define INVALID_SOCKET (-1) |
||
647 |
|||
648 |
#define to64(x) strtoll(x, NULL, 10) |
||
649 |
#define INT64_FMT PRId64 |
||
650 |
#define INT64_X_FMT PRIx64 |
||
651 |
#define SIZE_T_FMT "u" |
||
652 |
|||
653 |
#define SOMAXCONN 8 |
||
654 |
|||
655 |
const char *inet_ntop(int af, const void *src, char *dst, socklen_t size); |
||
656 |
char *inet_ntoa(struct in_addr in); |
||
657 |
int inet_pton(int af, const char *src, void *dst); |
||
658 |
|||
659 |
#endif /* CS_PLATFORM == CS_P_CC3100 */ |
||
660 |
#endif /* CS_COMMON_PLATFORMS_PLATFORM_CC3100_H_ */ |
||
661 |
#ifdef MJS_MODULE_LINES |
||
662 |
#line 1 "common/platforms/simplelink/cs_simplelink.h" |
||
663 |
#endif |
||
664 |
/* |
||
665 |
* Copyright (c) 2014-2018 Cesanta Software Limited |
||
666 |
* All rights reserved |
||
667 |
* |
||
668 |
* Licensed under the Apache License, Version 2.0 (the ""License""); |
||
669 |
* you may not use this file except in compliance with the License. |
||
670 |
* You may obtain a copy of the License at |
||
671 |
* |
||
672 |
* http://www.apache.org/licenses/LICENSE-2.0 |
||
673 |
* |
||
674 |
* Unless required by applicable law or agreed to in writing, software |
||
675 |
* distributed under the License is distributed on an ""AS IS"" BASIS, |
||
676 |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||
677 |
* See the License for the specific language governing permissions and |
||
678 |
* limitations under the License. |
||
679 |
*/ |
||
680 |
|||
681 |
#ifndef CS_COMMON_PLATFORMS_SIMPLELINK_CS_SIMPLELINK_H_ |
||
682 |
#define CS_COMMON_PLATFORMS_SIMPLELINK_CS_SIMPLELINK_H_ |
||
683 |
|||
684 |
#if defined(MG_NET_IF) && MG_NET_IF == MG_NET_IF_SIMPLELINK |
||
685 |
|||
686 |
/* If simplelink.h is already included, all bets are off. */ |
||
687 |
#if !defined(__SIMPLELINK_H__) |
||
688 |
|||
689 |
#include <stdbool.h> |
||
690 |
|||
691 |
#ifndef __TI_COMPILER_VERSION__ |
||
692 |
#undef __CONCAT |
||
693 |
#undef FD_CLR |
||
694 |
#undef FD_ISSET |
||
695 |
#undef FD_SET |
||
696 |
#undef FD_SETSIZE |
||
697 |
#undef FD_ZERO |
||
698 |
#undef fd_set |
||
699 |
#endif |
||
700 |
|||
701 |
#if CS_PLATFORM == CS_P_CC3220 |
||
702 |
#include <ti/drivers/net/wifi/porting/user.h> |
||
703 |
#include <ti/drivers/net/wifi/simplelink.h> |
||
704 |
#include <ti/drivers/net/wifi/sl_socket.h> |
||
705 |
#include <ti/drivers/net/wifi/netapp.h> |
||
706 |
#else |
||
707 |
/* We want to disable SL_INC_STD_BSD_API_NAMING, so we include user.h ourselves |
||
708 |
* and undef it. */ |
||
709 |
#define PROVISIONING_API_H_ |
||
710 |
#include <simplelink/user.h> |
||
711 |
#undef PROVISIONING_API_H_ |
||
712 |
#undef SL_INC_STD_BSD_API_NAMING |
||
713 |
|||
714 |
#include <simplelink/include/simplelink.h> |
||
715 |
#include <simplelink/include/netapp.h> |
||
716 |
#endif /* CS_PLATFORM == CS_P_CC3220 */ |
||
717 |
|||
718 |
/* Now define only the subset of the BSD API that we use. |
||
719 |
* Notably, close(), read() and write() are not defined. */ |
||
720 |
#define AF_INET SL_AF_INET |
||
721 |
|||
722 |
#define socklen_t SlSocklen_t |
||
723 |
#define sockaddr SlSockAddr_t |
||
724 |
#define sockaddr_in SlSockAddrIn_t |
||
725 |
#define in_addr SlInAddr_t |
||
726 |
|||
727 |
#define SOCK_STREAM SL_SOCK_STREAM |
||
728 |
#define SOCK_DGRAM SL_SOCK_DGRAM |
||
729 |
|||
730 |
#define htonl sl_Htonl |
||
731 |
#define ntohl sl_Ntohl |
||
732 |
#define htons sl_Htons |
||
733 |
#define ntohs sl_Ntohs |
||
734 |
|||
735 |
#ifndef EACCES |
||
736 |
#define EACCES SL_EACCES |
||
737 |
#endif |
||
738 |
#ifndef EAFNOSUPPORT |
||
739 |
#define EAFNOSUPPORT SL_EAFNOSUPPORT |
||
740 |
#endif |
||
741 |
#ifndef EAGAIN |
||
742 |
#define EAGAIN SL_EAGAIN |
||
743 |
#endif |
||
744 |
#ifndef EBADF |
||
745 |
#define EBADF SL_EBADF |
||
746 |
#endif |
||
747 |
#ifndef EINVAL |
||
748 |
#define EINVAL SL_EINVAL |
||
749 |
#endif |
||
750 |
#ifndef ENOMEM |
||
751 |
#define ENOMEM SL_ENOMEM |
||
752 |
#endif |
||
753 |
#ifndef EWOULDBLOCK |
||
754 |
#define EWOULDBLOCK SL_EWOULDBLOCK |
||
755 |
#endif |
||
756 |
|||
757 |
#define SOMAXCONN 8 |
||
758 |
|||
759 |
#ifdef __cplusplus |
||
760 |
extern "C" { |
||
761 |
#endif |
||
762 |
|||
763 |
const char *inet_ntop(int af, const void *src, char *dst, socklen_t size); |
||
764 |
char *inet_ntoa(struct in_addr in); |
||
765 |
int inet_pton(int af, const char *src, void *dst); |
||
766 |
|||
767 |
struct mg_mgr; |
||
768 |
struct mg_connection; |
||
769 |
|||
770 |
typedef void (*mg_init_cb)(struct mg_mgr *mgr); |
||
771 |
bool mg_start_task(int priority, int stack_size, mg_init_cb mg_init); |
||
772 |
|||
773 |
void mg_run_in_task(void (*cb)(struct mg_mgr *mgr, void *arg), void *cb_arg); |
||
774 |
|||
775 |
int sl_fs_init(void); |
||
776 |
|||
777 |
void sl_restart_cb(struct mg_mgr *mgr); |
||
778 |
|||
779 |
int sl_set_ssl_opts(int sock, struct mg_connection *nc); |
||
780 |
|||
781 |
#ifdef __cplusplus |
||
782 |
} |
||
783 |
#endif |
||
784 |
|||
785 |
#endif /* !defined(__SIMPLELINK_H__) */ |
||
786 |
|||
787 |
/* Compatibility with older versions of SimpleLink */ |
||
788 |
#if SL_MAJOR_VERSION_NUM < 2 |
||
789 |
|||
790 |
#define SL_ERROR_BSD_EAGAIN SL_EAGAIN |
||
791 |
#define SL_ERROR_BSD_EALREADY SL_EALREADY |
||
792 |
#define SL_ERROR_BSD_ENOPROTOOPT SL_ENOPROTOOPT |
||
793 |
#define SL_ERROR_BSD_ESECDATEERROR SL_ESECDATEERROR |
||
794 |
#define SL_ERROR_BSD_ESECSNOVERIFY SL_ESECSNOVERIFY |
||
795 |
#define SL_ERROR_FS_FAILED_TO_ALLOCATE_MEM SL_FS_ERR_FAILED_TO_ALLOCATE_MEM |
||
796 |
#define SL_ERROR_FS_FILE_HAS_NOT_BEEN_CLOSE_CORRECTLY \ |
||
797 |
SL_FS_FILE_HAS_NOT_BEEN_CLOSE_CORRECTLY |
||
798 |
#define SL_ERROR_FS_FILE_NAME_EXIST SL_FS_FILE_NAME_EXIST |
||
799 |
#define SL_ERROR_FS_FILE_NOT_EXISTS SL_FS_ERR_FILE_NOT_EXISTS |
||
800 |
#define SL_ERROR_FS_NO_AVAILABLE_NV_INDEX SL_FS_ERR_NO_AVAILABLE_NV_INDEX |
||
801 |
#define SL_ERROR_FS_NOT_ENOUGH_STORAGE_SPACE SL_FS_ERR_NO_AVAILABLE_BLOCKS |
||
802 |
#define SL_ERROR_FS_NOT_SUPPORTED SL_FS_ERR_NOT_SUPPORTED |
||
803 |
#define SL_ERROR_FS_WRONG_FILE_NAME SL_FS_WRONG_FILE_NAME |
||
804 |
#define SL_ERROR_FS_INVALID_HANDLE SL_FS_ERR_INVALID_HANDLE |
||
805 |
#define SL_NETCFG_MAC_ADDRESS_GET SL_MAC_ADDRESS_GET |
||
806 |
#define SL_SOCKET_FD_ZERO SL_FD_ZERO |
||
807 |
#define SL_SOCKET_FD_SET SL_FD_SET |
||
808 |
#define SL_SOCKET_FD_ISSET SL_FD_ISSET |
||
809 |
#define SL_SO_SECURE_DOMAIN_NAME_VERIFICATION SO_SECURE_DOMAIN_NAME_VERIFICATION |
||
810 |
|||
811 |
#define SL_FS_READ FS_MODE_OPEN_READ |
||
812 |
#define SL_FS_WRITE FS_MODE_OPEN_WRITE |
||
813 |
|||
814 |
#define SL_FI_FILE_SIZE(fi) ((fi).FileLen) |
||
815 |
#define SL_FI_FILE_MAX_SIZE(fi) ((fi).AllocatedLen) |
||
816 |
|||
817 |
#define SlDeviceVersion_t SlVersionFull |
||
818 |
#define sl_DeviceGet sl_DevGet |
||
819 |
#define SL_DEVICE_GENERAL SL_DEVICE_GENERAL_CONFIGURATION |
||
820 |
#define SL_LEN_TYPE _u8 |
||
821 |
#define SL_OPT_TYPE _u8 |
||
822 |
|||
823 |
#else /* SL_MAJOR_VERSION_NUM >= 2 */ |
||
824 |
|||
825 |
#define FS_MODE_OPEN_CREATE(max_size, flag) \ |
||
826 |
(SL_FS_CREATE | SL_FS_CREATE_MAX_SIZE(max_size)) |
||
827 |
#define SL_FI_FILE_SIZE(fi) ((fi).Len) |
||
828 |
#define SL_FI_FILE_MAX_SIZE(fi) ((fi).MaxSize) |
||
829 |
|||
830 |
#define SL_LEN_TYPE _u16 |
||
831 |
#define SL_OPT_TYPE _u16 |
||
832 |
|||
833 |
#endif /* SL_MAJOR_VERSION_NUM < 2 */ |
||
834 |
|||
835 |
int slfs_open(const unsigned char *fname, uint32_t flags); |
||
836 |
|||
837 |
#endif /* MG_NET_IF == MG_NET_IF_SIMPLELINK */ |
||
838 |
|||
839 |
#endif /* CS_COMMON_PLATFORMS_SIMPLELINK_CS_SIMPLELINK_H_ */ |
||
840 |
#ifdef MJS_MODULE_LINES |
||
841 |
#line 1 "common/platforms/platform_cc3200.h" |
||
842 |
#endif |
||
843 |
/* |
||
844 |
* Copyright (c) 2014-2018 Cesanta Software Limited |
||
845 |
* All rights reserved |
||
846 |
* |
||
847 |
* Licensed under the Apache License, Version 2.0 (the ""License""); |
||
848 |
* you may not use this file except in compliance with the License. |
||
849 |
* You may obtain a copy of the License at |
||
850 |
* |
||
851 |
* http://www.apache.org/licenses/LICENSE-2.0 |
||
852 |
* |
||
853 |
* Unless required by applicable law or agreed to in writing, software |
||
854 |
* distributed under the License is distributed on an ""AS IS"" BASIS, |
||
855 |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||
856 |
* See the License for the specific language governing permissions and |
||
857 |
* limitations under the License. |
||
858 |
*/ |
||
859 |
|||
860 |
#ifndef CS_COMMON_PLATFORMS_PLATFORM_CC3200_H_ |
||
861 |
#define CS_COMMON_PLATFORMS_PLATFORM_CC3200_H_ |
||
862 |
#if CS_PLATFORM == CS_P_CC3200 |
||
863 |
|||
864 |
#include <assert.h> |
||
865 |
#include <ctype.h> |
||
866 |
#include <errno.h> |
||
867 |
#include <inttypes.h> |
||
868 |
#include <stdbool.h> |
||
869 |
#include <stdint.h> |
||
870 |
#include <string.h> |
||
871 |
#include <time.h> |
||
872 |
|||
873 |
#ifndef __TI_COMPILER_VERSION__ |
||
874 |
#include <fcntl.h> |
||
875 |
#include <sys/time.h> |
||
876 |
#endif |
||
877 |
|||
878 |
#define MG_NET_IF MG_NET_IF_SIMPLELINK |
||
879 |
#define MG_SSL_IF MG_SSL_IF_SIMPLELINK |
||
880 |
|||
881 |
/* Only SPIFFS supports directories, SLFS does not. */ |
||
882 |
#if defined(CC3200_FS_SPIFFS) && !defined(MG_ENABLE_DIRECTORY_LISTING) |
||
883 |
#define MG_ENABLE_DIRECTORY_LISTING 1 |
||
884 |
#endif |
||
885 |
|||
886 |
/* Amalgamated: #include "common/platforms/simplelink/cs_simplelink.h" */ |
||
887 |
|||
888 |
typedef int sock_t; |
||
889 |
#define INVALID_SOCKET (-1) |
||
890 |
#define SIZE_T_FMT "u" |
||
891 |
typedef struct stat cs_stat_t; |
||
892 |
#define DIRSEP '/' |
||
893 |
#define to64(x) strtoll(x, NULL, 10) |
||
894 |
#define INT64_FMT PRId64 |
||
895 |
#define INT64_X_FMT PRIx64 |
||
896 |
#define __cdecl |
||
897 |
|||
898 |
#define fileno(x) -1 |
||
899 |
|||
900 |
/* Some functions we implement for Mongoose. */ |
||
901 |
|||
902 |
#ifdef __cplusplus |
||
903 |
extern "C" { |
||
904 |
#endif |
||
905 |
|||
906 |
#ifdef __TI_COMPILER_VERSION__ |
||
907 |
struct SlTimeval_t; |
||
908 |
#define timeval SlTimeval_t |
||
909 |
int gettimeofday(struct timeval *t, void *tz); |
||
910 |
int settimeofday(const struct timeval *tv, const void *tz); |
||
911 |
|||
912 |
int asprintf(char **strp, const char *fmt, ...); |
||
913 |
|||
914 |
#endif |
||
915 |
|||
916 |
/* TI's libc does not have stat & friends, add them. */ |
||
917 |
#ifdef __TI_COMPILER_VERSION__ |
||
918 |
|||
919 |
#include <file.h> |
||
920 |
|||
921 |
typedef unsigned int mode_t; |
||
922 |
typedef size_t _off_t; |
||
923 |
typedef long ssize_t; |
||
924 |
|||
925 |
struct stat { |
||
926 |
int st_ino; |
||
927 |
mode_t st_mode; |
||
928 |
int st_nlink; |
||
929 |
time_t st_mtime; |
||
930 |
off_t st_size; |
||
931 |
}; |
||
932 |
|||
933 |
int _stat(const char *pathname, struct stat *st); |
||
934 |
int stat(const char *pathname, struct stat *st); |
||
935 |
|||
936 |
#define __S_IFMT 0170000 |
||
937 |
|||
938 |
#define __S_IFDIR 0040000 |
||
939 |
#define __S_IFCHR 0020000 |
||
940 |
#define __S_IFREG 0100000 |
||
941 |
|||
942 |
#define __S_ISTYPE(mode, mask) (((mode) &__S_IFMT) == (mask)) |
||
943 |
|||
944 |
#define S_IFDIR __S_IFDIR |
||
945 |
#define S_IFCHR __S_IFCHR |
||
946 |
#define S_IFREG __S_IFREG |
||
947 |
#define S_ISDIR(mode) __S_ISTYPE((mode), __S_IFDIR) |
||
948 |
#define S_ISREG(mode) __S_ISTYPE((mode), __S_IFREG) |
||
949 |
|||
950 |
/* 5.x series compilers don't have va_copy, 16.x do. */ |
||
951 |
#if __TI_COMPILER_VERSION__ < 16000000 |
||
952 |
#define va_copy(apc, ap) ((apc) = (ap)) |
||
953 |
#endif |
||
954 |
|||
955 |
#endif /* __TI_COMPILER_VERSION__ */ |
||
956 |
|||
957 |
#ifdef CC3200_FS_SLFS |
||
958 |
#define MG_FS_SLFS |
||
959 |
#endif |
||
960 |
|||
961 |
#if (defined(CC3200_FS_SPIFFS) || defined(CC3200_FS_SLFS)) && \ |
||
962 |
!defined(MG_ENABLE_FILESYSTEM) |
||
963 |
#define MG_ENABLE_FILESYSTEM 1 |
||
964 |
#define CS_DEFINE_DIRENT |
||
965 |
#endif |
||
966 |
|||
967 |
#ifndef CS_ENABLE_STDIO |
||
968 |
#define CS_ENABLE_STDIO 1 |
||
969 |
#endif |
||
970 |
|||
971 |
#ifdef __cplusplus |
||
972 |
} |
||
973 |
#endif |
||
974 |
|||
975 |
#endif /* CS_PLATFORM == CS_P_CC3200 */ |
||
976 |
#endif /* CS_COMMON_PLATFORMS_PLATFORM_CC3200_H_ */ |
||
977 |
#ifdef MJS_MODULE_LINES |
||
978 |
#line 1 "common/platforms/platform_cc3220.h" |
||
979 |
#endif |
||
980 |
/* |
||
981 |
* Copyright (c) 2014-2018 Cesanta Software Limited |
||
982 |
* All rights reserved |
||
983 |
* |
||
984 |
* Licensed under the Apache License, Version 2.0 (the ""License""); |
||
985 |
* you may not use this file except in compliance with the License. |
||
986 |
* You may obtain a copy of the License at |
||
987 |
* |
||
988 |
* http://www.apache.org/licenses/LICENSE-2.0 |
||
989 |
* |
||
990 |
* Unless required by applicable law or agreed to in writing, software |
||
991 |
* distributed under the License is distributed on an ""AS IS"" BASIS, |
||
992 |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||
993 |
* See the License for the specific language governing permissions and |
||
994 |
* limitations under the License. |
||
995 |
*/ |
||
996 |
|||
997 |
#ifndef CS_COMMON_PLATFORMS_PLATFORM_CC3220_H_ |
||
998 |
#define CS_COMMON_PLATFORMS_PLATFORM_CC3220_H_ |
||
999 |
#if CS_PLATFORM == CS_P_CC3220 |
||
1000 |
|||
1001 |
#include <assert.h> |
||
1002 |
#include <ctype.h> |
||
1003 |
#include <errno.h> |
||
1004 |
#include <inttypes.h> |
||
1005 |
#include <stdbool.h> |
||
1006 |
#include <stdint.h> |
||
1007 |
#include <string.h> |
||
1008 |
#include <time.h> |
||
1009 |
|||
1010 |
#ifndef __TI_COMPILER_VERSION__ |
||
1011 |
#include <fcntl.h> |
||
1012 |
#include <sys/time.h> |
||
1013 |
#endif |
||
1014 |
|||
1015 |
#define MG_NET_IF MG_NET_IF_SIMPLELINK |
||
1016 |
#ifndef MG_SSL_IF |
||
1017 |
#define MG_SSL_IF MG_SSL_IF_SIMPLELINK |
||
1018 |
#endif |
||
1019 |
|||
1020 |
/* Only SPIFFS supports directories, SLFS does not. */ |
||
1021 |
#if defined(CC3220_FS_SPIFFS) && !defined(MG_ENABLE_DIRECTORY_LISTING) |
||
1022 |
#define MG_ENABLE_DIRECTORY_LISTING 1 |
||
1023 |
#endif |
||
1024 |
|||
1025 |
/* Amalgamated: #include "common/platforms/simplelink/cs_simplelink.h" */ |
||
1026 |
|||
1027 |
typedef int sock_t; |
||
1028 |
#define INVALID_SOCKET (-1) |
||
1029 |
#define SIZE_T_FMT "u" |
||
1030 |
typedef struct stat cs_stat_t; |
||
1031 |
#define DIRSEP '/' |
||
1032 |
#define to64(x) strtoll(x, NULL, 10) |
||
1033 |
#define INT64_FMT PRId64 |
||
1034 |
#define INT64_X_FMT PRIx64 |
||
1035 |
#define __cdecl |
||
1036 |
|||
1037 |
#define fileno(x) -1 |
||
1038 |
|||
1039 |
/* Some functions we implement for Mongoose. */ |
||
1040 |
|||
1041 |
#ifdef __cplusplus |
||
1042 |
extern "C" { |
||
1043 |
#endif |
||
1044 |
|||
1045 |
#ifdef __TI_COMPILER_VERSION__ |
||
1046 |
struct SlTimeval_t; |
||
1047 |
#define timeval SlTimeval_t |
||
1048 |
int gettimeofday(struct timeval *t, void *tz); |
||
1049 |
int settimeofday(const struct timeval *tv, const void *tz); |
||
1050 |
|||
1051 |
int asprintf(char **strp, const char *fmt, ...); |
||
1052 |
|||
1053 |
#endif |
||
1054 |
|||
1055 |
/* TI's libc does not have stat & friends, add them. */ |
||
1056 |
#ifdef __TI_COMPILER_VERSION__ |
||
1057 |
|||
1058 |
#include <file.h> |
||
1059 |
|||
1060 |
typedef unsigned int mode_t; |
||
1061 |
typedef size_t _off_t; |
||
1062 |
typedef long ssize_t; |
||
1063 |
|||
1064 |
struct stat { |
||
1065 |
int st_ino; |
||
1066 |
mode_t st_mode; |
||
1067 |
int st_nlink; |
||
1068 |
time_t st_mtime; |
||
1069 |
off_t st_size; |
||
1070 |
}; |
||
1071 |
|||
1072 |
int _stat(const char *pathname, struct stat *st); |
||
1073 |
int stat(const char *pathname, struct stat *st); |
||
1074 |
|||
1075 |
#define __S_IFMT 0170000 |
||
1076 |
|||
1077 |
#define __S_IFDIR 0040000 |
||
1078 |
#define __S_IFCHR 0020000 |
||
1079 |
#define __S_IFREG 0100000 |
||
1080 |
|||
1081 |
#define __S_ISTYPE(mode, mask) (((mode) &__S_IFMT) == (mask)) |
||
1082 |
|||
1083 |
#define S_IFDIR __S_IFDIR |
||
1084 |
#define S_IFCHR __S_IFCHR |
||
1085 |
#define S_IFREG __S_IFREG |
||
1086 |
#define S_ISDIR(mode) __S_ISTYPE((mode), __S_IFDIR) |
||
1087 |
#define S_ISREG(mode) __S_ISTYPE((mode), __S_IFREG) |
||
1088 |
|||
1089 |
#endif /* __TI_COMPILER_VERSION__ */ |
||
1090 |
|||
1091 |
#ifndef CS_ENABLE_STDIO |
||
1092 |
#define CS_ENABLE_STDIO 1 |
||
1093 |
#endif |
||
1094 |
|||
1095 |
#ifdef __cplusplus |
||
1096 |
} |
||
1097 |
#endif |
||
1098 |
|||
1099 |
#endif /* CS_PLATFORM == CS_P_CC3220 */ |
||
1100 |
#endif /* CS_COMMON_PLATFORMS_PLATFORM_CC3200_H_ */ |
||
1101 |
#ifdef MJS_MODULE_LINES |
||
1102 |
#line 1 "common/platforms/platform_mbed.h" |
||
1103 |
#endif |
||
1104 |
/* |
||
1105 |
* Copyright (c) 2014-2018 Cesanta Software Limited |
||
1106 |
* All rights reserved |
||
1107 |
* |
||
1108 |
* Licensed under the Apache License, Version 2.0 (the ""License""); |
||
1109 |
* you may not use this file except in compliance with the License. |
||
1110 |
* You may obtain a copy of the License at |
||
1111 |
* |
||
1112 |
* http://www.apache.org/licenses/LICENSE-2.0 |
||
1113 |
* |
||
1114 |
* Unless required by applicable law or agreed to in writing, software |
||
1115 |
* distributed under the License is distributed on an ""AS IS"" BASIS, |
||
1116 |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||
1117 |
* See the License for the specific language governing permissions and |
||
1118 |
* limitations under the License. |
||
1119 |
*/ |
||
1120 |
|||
1121 |
#ifndef CS_COMMON_PLATFORMS_PLATFORM_MBED_H_ |
||
1122 |
#define CS_COMMON_PLATFORMS_PLATFORM_MBED_H_ |
||
1123 |
#if CS_PLATFORM == CS_P_MBED |
||
1124 |
|||
1125 |
/* |
||
1126 |
* mbed.h contains C++ code (e.g. templates), thus, it should be processed |
||
1127 |
* only if included directly to startup file (ex: main.cpp) |
||
1128 |
*/ |
||
1129 |
#ifdef __cplusplus |
||
1130 |
/* Amalgamated: #include "mbed.h" */ |
||
1131 |
#endif /* __cplusplus */ |
||
1132 |
|||
1133 |
#include <assert.h> |
||
1134 |
#include <ctype.h> |
||
1135 |
#include <errno.h> |
||
1136 |
#include <inttypes.h> |
||
1137 |
#include <stdint.h> |
||
1138 |
#include <string.h> |
||
1139 |
#include <time.h> |
||
1140 |
#include <sys/stat.h> |
||
1141 |
#include <sys/types.h> |
||
1142 |
#include <fcntl.h> |
||
1143 |
#include <stdio.h> |
||
1144 |
|||
1145 |
typedef struct stat cs_stat_t; |
||
1146 |
#define DIRSEP '/' |
||
1147 |
|||
1148 |
#ifndef CS_ENABLE_STDIO |
||
1149 |
#define CS_ENABLE_STDIO 1 |
||
1150 |
#endif |
||
1151 |
|||
1152 |
/* |
||
1153 |
* mbed can be compiled with the ARM compiler which |
||
1154 |
* just doesn't come with a gettimeofday shim |
||
1155 |
* because it's a BSD API and ARM targets embedded |
||
1156 |
* non-unix platforms. |
||
1157 |
*/ |
||
1158 |
#if defined(__ARMCC_VERSION) || defined(__ICCARM__) |
||
1159 |
#define _TIMEVAL_DEFINED |
||
1160 |
#define gettimeofday _gettimeofday |
||
1161 |
|||
1162 |
/* copied from GCC on ARM; for some reason useconds are signed */ |
||
1163 |
typedef long suseconds_t; /* microseconds (signed) */ |
||
1164 |
struct timeval { |
||
1165 |
time_t tv_sec; /* seconds */ |
||
1166 |
suseconds_t tv_usec; /* and microseconds */ |
||
1167 |
}; |
||
1168 |
|||
1169 |
#endif |
||
1170 |
|||
1171 |
#if MG_NET_IF == MG_NET_IF_SIMPLELINK |
||
1172 |
|||
1173 |
#define MG_SIMPLELINK_NO_OSI 1 |
||
1174 |
|||
1175 |
#include <simplelink.h> |
||
1176 |
|||
1177 |
typedef int sock_t; |
||
1178 |
#define INVALID_SOCKET (-1) |
||
1179 |
|||
1180 |
#define to64(x) strtoll(x, NULL, 10) |
||
1181 |
#define INT64_FMT PRId64 |
||
1182 |
#define INT64_X_FMT PRIx64 |
||
1183 |
#define SIZE_T_FMT "u" |
||
1184 |
|||
1185 |
#define SOMAXCONN 8 |
||
1186 |
|||
1187 |
const char *inet_ntop(int af, const void *src, char *dst, socklen_t size); |
||
1188 |
char *inet_ntoa(struct in_addr in); |
||
1189 |
int inet_pton(int af, const char *src, void *dst); |
||
1190 |
int inet_aton(const char *cp, struct in_addr *inp); |
||
1191 |
in_addr_t inet_addr(const char *cp); |
||
1192 |
|||
1193 |
#endif /* MG_NET_IF == MG_NET_IF_SIMPLELINK */ |
||
1194 |
|||
1195 |
#endif /* CS_PLATFORM == CS_P_MBED */ |
||
1196 |
#endif /* CS_COMMON_PLATFORMS_PLATFORM_MBED_H_ */ |
||
1197 |
#ifdef MJS_MODULE_LINES |
||
1198 |
#line 1 "common/platforms/platform_nrf51.h" |
||
1199 |
#endif |
||
1200 |
/* |
||
1201 |
* Copyright (c) 2014-2018 Cesanta Software Limited |
||
1202 |
* All rights reserved |
||
1203 |
* |
||
1204 |
* Licensed under the Apache License, Version 2.0 (the ""License""); |
||
1205 |
* you may not use this file except in compliance with the License. |
||
1206 |
* You may obtain a copy of the License at |
||
1207 |
* |
||
1208 |
* http://www.apache.org/licenses/LICENSE-2.0 |
||
1209 |
* |
||
1210 |
* Unless required by applicable law or agreed to in writing, software |
||
1211 |
* distributed under the License is distributed on an ""AS IS"" BASIS, |
||
1212 |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||
1213 |
* See the License for the specific language governing permissions and |
||
1214 |
* limitations under the License. |
||
1215 |
*/ |
||
1216 |
#ifndef CS_COMMON_PLATFORMS_PLATFORM_NRF51_H_ |
||
1217 |
#define CS_COMMON_PLATFORMS_PLATFORM_NRF51_H_ |
||
1218 |
#if CS_PLATFORM == CS_P_NRF51 |
||
1219 |
|||
1220 |
#include <assert.h> |
||
1221 |
#include <ctype.h> |
||
1222 |
#include <inttypes.h> |
||
1223 |
#include <stdint.h> |
||
1224 |
#include <string.h> |
||
1225 |
#include <time.h> |
||
1226 |
|||
1227 |
#define to64(x) strtoll(x, NULL, 10) |
||
1228 |
|||
1229 |
#define MG_NET_IF MG_NET_IF_LWIP_LOW_LEVEL |
||
1230 |
#define MG_LWIP 1 |
||
1231 |
#define MG_ENABLE_IPV6 1 |
||
1232 |
|||
1233 |
/* |
||
1234 |
* For ARM C Compiler, make lwip to export `struct timeval`; for other |
||
1235 |
* compilers, suppress it. |
||
1236 |
*/ |
||
1237 |
#if !defined(__ARMCC_VERSION) |
||
1238 |
#define LWIP_TIMEVAL_PRIVATE 0 |
||
1239 |
#else |
||
1240 |
struct timeval; |
||
1241 |
int gettimeofday(struct timeval *tp, void *tzp); |
||
1242 |
#endif |
||
1243 |
|||
1244 |
#define INT64_FMT PRId64 |
||
1245 |
#define SIZE_T_FMT "u" |
||
1246 |
|||
1247 |
/* |
||
1248 |
* ARM C Compiler doesn't have strdup, so we provide it |
||
1249 |
*/ |
||
1250 |
#define CS_ENABLE_STRDUP defined(__ARMCC_VERSION) |
||
1251 |
|||
1252 |
#endif /* CS_PLATFORM == CS_P_NRF51 */ |
||
1253 |
#endif /* CS_COMMON_PLATFORMS_PLATFORM_NRF51_H_ */ |
||
1254 |
#ifdef MJS_MODULE_LINES |
||
1255 |
#line 1 "common/platforms/platform_nrf52.h" |
||
1256 |
#endif |
||
1257 |
/* |
||
1258 |
* Copyright (c) 2014-2018 Cesanta Software Limited |
||
1259 |
* All rights reserved |
||
1260 |
* |
||
1261 |
* Licensed under the Apache License, Version 2.0 (the ""License""); |
||
1262 |
* you may not use this file except in compliance with the License. |
||
1263 |
* You may obtain a copy of the License at |
||
1264 |
* |
||
1265 |
* http://www.apache.org/licenses/LICENSE-2.0 |
||
1266 |
* |
||
1267 |
* Unless required by applicable law or agreed to in writing, software |
||
1268 |
* distributed under the License is distributed on an ""AS IS"" BASIS, |
||
1269 |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||
1270 |
* See the License for the specific language governing permissions and |
||
1271 |
* limitations under the License. |
||
1272 |
*/ |
||
1273 |
#ifndef CS_COMMON_PLATFORMS_PLATFORM_NRF52_H_ |
||
1274 |
#define CS_COMMON_PLATFORMS_PLATFORM_NRF52_H_ |
||
1275 |
#if CS_PLATFORM == CS_P_NRF52 |
||
1276 |
|||
1277 |
#include <assert.h> |
||
1278 |
#include <ctype.h> |
||
1279 |
#include <errno.h> |
||
1280 |
#include <inttypes.h> |
||
1281 |
#include <stdbool.h> |
||
1282 |
#include <stdint.h> |
||
1283 |
#include <string.h> |
||
1284 |
#include <time.h> |
||
1285 |
|||
1286 |
#define to64(x) strtoll(x, NULL, 10) |
||
1287 |
|||
1288 |
#define MG_NET_IF MG_NET_IF_LWIP_LOW_LEVEL |
||
1289 |
#define MG_LWIP 1 |
||
1290 |
#define MG_ENABLE_IPV6 1 |
||
1291 |
|||
1292 |
#if !defined(ENOSPC) |
||
1293 |
#define ENOSPC 28 /* No space left on device */ |
||
1294 |
#endif |
||
1295 |
|||
1296 |
/* |
||
1297 |
* For ARM C Compiler, make lwip to export `struct timeval`; for other |
||
1298 |
* compilers, suppress it. |
||
1299 |
*/ |
||
1300 |
#if !defined(__ARMCC_VERSION) |
||
1301 |
#define LWIP_TIMEVAL_PRIVATE 0 |
||
1302 |
#endif |
||
1303 |
|||
1304 |
#define INT64_FMT PRId64 |
||
1305 |
#define SIZE_T_FMT "u" |
||
1306 |
|||
1307 |
/* |
||
1308 |
* ARM C Compiler doesn't have strdup, so we provide it |
||
1309 |
*/ |
||
1310 |
#define CS_ENABLE_STRDUP defined(__ARMCC_VERSION) |
||
1311 |
|||
1312 |
#endif /* CS_PLATFORM == CS_P_NRF52 */ |
||
1313 |
#endif /* CS_COMMON_PLATFORMS_PLATFORM_NRF52_H_ */ |
||
1314 |
#ifdef MJS_MODULE_LINES |
||
1315 |
#line 1 "common/platforms/platform_wince.h" |
||
1316 |
#endif |
||
1317 |
#ifndef CS_COMMON_PLATFORMS_PLATFORM_WINCE_H_ |
||
1318 |
#define CS_COMMON_PLATFORMS_PLATFORM_WINCE_H_ |
||
1319 |
|||
1320 |
#if CS_PLATFORM == CS_P_WINCE |
||
1321 |
|||
1322 |
/* |
||
1323 |
* MSVC++ 14.0 _MSC_VER == 1900 (Visual Studio 2015) |
||
1324 |
* MSVC++ 12.0 _MSC_VER == 1800 (Visual Studio 2013) |
||
1325 |
* MSVC++ 11.0 _MSC_VER == 1700 (Visual Studio 2012) |
||
1326 |
* MSVC++ 10.0 _MSC_VER == 1600 (Visual Studio 2010) |
||
1327 |
* MSVC++ 9.0 _MSC_VER == 1500 (Visual Studio 2008) |
||
1328 |
* MSVC++ 8.0 _MSC_VER == 1400 (Visual Studio 2005) |
||
1329 |
* MSVC++ 7.1 _MSC_VER == 1310 (Visual Studio 2003) |
||
1330 |
* MSVC++ 7.0 _MSC_VER == 1300 |
||
1331 |
* MSVC++ 6.0 _MSC_VER == 1200 |
||
1332 |
* MSVC++ 5.0 _MSC_VER == 1100 |
||
1333 |
*/ |
||
1334 |
#pragma warning(disable : 4127) /* FD_SET() emits warning, disable it */ |
||
1335 |
#pragma warning(disable : 4204) /* missing c99 support */ |
||
1336 |
|||
1337 |
#ifndef _WINSOCK_DEPRECATED_NO_WARNINGS |
||
1338 |
#define _WINSOCK_DEPRECATED_NO_WARNINGS 1 |
||
1339 |
#endif |
||
1340 |
|||
1341 |
#ifndef _CRT_SECURE_NO_WARNINGS |
||
1342 |
#define _CRT_SECURE_NO_WARNINGS |
||
1343 |
#endif |
||
1344 |
|||
1345 |
#include <assert.h> |
||
1346 |
#include <limits.h> |
||
1347 |
#include <stddef.h> |
||
1348 |
#include <stdio.h> |
||
1349 |
#include <stdlib.h> |
||
1350 |
#include <time.h> |
||
1351 |
|||
1352 |
#pragma comment(lib, "ws2.lib") /* Linking with WinCE winsock library */ |
||
1353 |
|||
1354 |
#include <winsock2.h> |
||
1355 |
#include <ws2tcpip.h> |
||
1356 |
#include <windows.h> |
||
1357 |
|||
1358 |
#define strdup _strdup |
||
1359 |
|||
1360 |
#ifndef EINPROGRESS |
||
1361 |
#define EINPROGRESS WSAEINPROGRESS |
||
1362 |
#endif |
||
1363 |
|||
1364 |
#ifndef EWOULDBLOCK |
||
1365 |
#define EWOULDBLOCK WSAEWOULDBLOCK |
||
1366 |
#endif |
||
1367 |
|||
1368 |
#ifndef EAGAIN |
||
1369 |
#define EAGAIN EWOULDBLOCK |
||
1370 |
#endif |
||
1371 |
|||
1372 |
#ifndef __func__ |
||
1373 |
#define STRX(x) #x |
||
1374 |
#define STR(x) STRX(x) |
||
1375 |
#define __func__ __FILE__ ":" STR(__LINE__) |
||
1376 |
#endif |
||
1377 |
|||
1378 |
#define snprintf _snprintf |
||
1379 |
#define fileno _fileno |
||
1380 |
#define vsnprintf _vsnprintf |
||
1381 |
#define sleep(x) Sleep((x) *1000) |
||
1382 |
#define to64(x) _atoi64(x) |
||
1383 |
#define rmdir _rmdir |
||
1384 |
|||
1385 |
#if defined(_MSC_VER) && _MSC_VER >= 1400 |
||
1386 |
#define fseeko(x, y, z) _fseeki64((x), (y), (z)) |
||
1387 |
#else |
||
1388 |
#define fseeko(x, y, z) fseek((x), (y), (z)) |
||
1389 |
#endif |
||
1390 |
|||
1391 |
typedef int socklen_t; |
||
1392 |
|||
1393 |
#if _MSC_VER >= 1700 |
||
1394 |
#include <stdint.h> |
||
1395 |
#else |
||
1396 |
typedef signed char int8_t; |
||
1397 |
typedef unsigned char uint8_t; |
||
1398 |
typedef int int32_t; |
||
1399 |
typedef unsigned int uint32_t; |
||
1400 |
typedef short int16_t; |
||
1401 |
typedef unsigned short uint16_t; |
||
1402 |
typedef __int64 int64_t; |
||
1403 |
typedef unsigned __int64 uint64_t; |
||
1404 |
#endif |
||
1405 |
|||
1406 |
typedef SOCKET sock_t; |
||
1407 |
typedef uint32_t in_addr_t; |
||
1408 |
|||
1409 |
#ifndef UINT16_MAX |
||
1410 |
#define UINT16_MAX 65535 |
||
1411 |
#endif |
||
1412 |
|||
1413 |
#ifndef UINT32_MAX |
||
1414 |
#define UINT32_MAX 4294967295 |
||
1415 |
#endif |
||
1416 |
|||
1417 |
#ifndef pid_t |
||
1418 |
#define pid_t HANDLE |
||
1419 |
#endif |
||
1420 |
|||
1421 |
#define INT64_FMT "I64d" |
||
1422 |
#define INT64_X_FMT "I64x" |
||
1423 |
/* TODO(alashkin): check if this is correct */ |
||
1424 |
#define SIZE_T_FMT "u" |
||
1425 |
|||
1426 |
#define DIRSEP '\\' |
||
1427 |
#define CS_DEFINE_DIRENT |
||
1428 |
|||
1429 |
#ifndef va_copy |
||
1430 |
#ifdef __va_copy |
||
1431 |
#define va_copy __va_copy |
||
1432 |
#else |
||
1433 |
#define va_copy(x, y) (x) = (y) |
||
1434 |
#endif |
||
1435 |
#endif |
||
1436 |
|||
1437 |
#ifndef MG_MAX_HTTP_REQUEST_SIZE |
||
1438 |
#define MG_MAX_HTTP_REQUEST_SIZE 8192 |
||
1439 |
#endif |
||
1440 |
|||
1441 |
#ifndef MG_MAX_HTTP_SEND_MBUF |
||
1442 |
#define MG_MAX_HTTP_SEND_MBUF 4096 |
||
1443 |
#endif |
||
1444 |
|||
1445 |
#ifndef MG_MAX_HTTP_HEADERS |
||
1446 |
#define MG_MAX_HTTP_HEADERS 40 |
||
1447 |
#endif |
||
1448 |
|||
1449 |
#ifndef CS_ENABLE_STDIO |
||
1450 |
#define CS_ENABLE_STDIO 1 |
||
1451 |
#endif |
||
1452 |
|||
1453 |
#define abort() DebugBreak(); |
||
1454 |
|||
1455 |
#ifndef BUFSIZ |
||
1456 |
#define BUFSIZ 4096 |
||
1457 |
#endif |
||
1458 |
/* |
||
1459 |
* Explicitly disabling MG_ENABLE_THREADS for WinCE |
||
1460 |
* because they are enabled for _WIN32 by default |
||
1461 |
*/ |
||
1462 |
#ifndef MG_ENABLE_THREADS |
||
1463 |
#define MG_ENABLE_THREADS 0 |
||
1464 |
#endif |
||
1465 |
|||
1466 |
#ifndef MG_ENABLE_FILESYSTEM |
||
1467 |
#define MG_ENABLE_FILESYSTEM 1 |
||
1468 |
#endif |
||
1469 |
|||
1470 |
#ifndef MG_NET_IF |
||
1471 |
#define MG_NET_IF MG_NET_IF_SOCKET |
||
1472 |
#endif |
||
1473 |
|||
1474 |
typedef struct _stati64 { |
||
1475 |
uint32_t st_mtime; |
||
1476 |
uint32_t st_size; |
||
1477 |
uint32_t st_mode; |
||
1478 |
} cs_stat_t; |
||
1479 |
|||
1480 |
/* |
||
1481 |
* WinCE 6.0 has a lot of useful definitions in ATL (not windows.h) headers |
||
1482 |
* use #ifdefs to avoid conflicts |
||
1483 |
*/ |
||
1484 |
|||
1485 |
#ifndef ENOENT |
||
1486 |
#define ENOENT ERROR_PATH_NOT_FOUND |
||
1487 |
#endif |
||
1488 |
|||
1489 |
#ifndef EACCES |
||
1490 |
#define EACCES ERROR_ACCESS_DENIED |
||
1491 |
#endif |
||
1492 |
|||
1493 |
#ifndef ENOMEM |
||
1494 |
#define ENOMEM ERROR_NOT_ENOUGH_MEMORY |
||
1495 |
#endif |
||
1496 |
|||
1497 |
#ifndef _UINTPTR_T_DEFINED |
||
1498 |
typedef unsigned int *uintptr_t; |
||
1499 |
#endif |
||
1500 |
|||
1501 |
#define _S_IFREG 2 |
||
1502 |
#define _S_IFDIR 4 |
||
1503 |
|||
1504 |
#ifndef S_ISDIR |
||
1505 |
#define S_ISDIR(x) (((x) &_S_IFDIR) != 0) |
||
1506 |
#endif |
||
1507 |
|||
1508 |
#ifndef S_ISREG |
||
1509 |
#define S_ISREG(x) (((x) &_S_IFREG) != 0) |
||
1510 |
#endif |
||
1511 |
|||
1512 |
int open(const char *filename, int oflag, int pmode); |
||
1513 |
int _wstati64(const wchar_t *path, cs_stat_t *st); |
||
1514 |
const char *strerror(); |
||
1515 |
|||
1516 |
#endif /* CS_PLATFORM == CS_P_WINCE */ |
||
1517 |
#endif /* CS_COMMON_PLATFORMS_PLATFORM_WINCE_H_ */ |
||
1518 |
#ifdef MJS_MODULE_LINES |
||
1519 |
#line 1 "common/platforms/platform_nxp_lpc.h" |
||
1520 |
#endif |
||
1521 |
/* |
||
1522 |
* Copyright (c) 2014-2018 Cesanta Software Limited |
||
1523 |
* All rights reserved |
||
1524 |
* |
||
1525 |
* Licensed under the Apache License, Version 2.0 (the ""License""); |
||
1526 |
* you may not use this file except in compliance with the License. |
||
1527 |
* You may obtain a copy of the License at |
||
1528 |
* |
||
1529 |
* http://www.apache.org/licenses/LICENSE-2.0 |
||
1530 |
* |
||
1531 |
* Unless required by applicable law or agreed to in writing, software |
||
1532 |
* distributed under the License is distributed on an ""AS IS"" BASIS, |
||
1533 |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||
1534 |
* See the License for the specific language governing permissions and |
||
1535 |
* limitations under the License. |
||
1536 |
*/ |
||
1537 |
|||
1538 |
#ifndef CS_COMMON_PLATFORMS_PLATFORM_NXP_LPC_H_ |
||
1539 |
#define CS_COMMON_PLATFORMS_PLATFORM_NXP_LPC_H_ |
||
1540 |
|||
1541 |
#if CS_PLATFORM == CS_P_NXP_LPC |
||
1542 |
|||
1543 |
#include <ctype.h> |
||
1544 |
#include <stdint.h> |
||
1545 |
#include <string.h> |
||
1546 |
|||
1547 |
#define SIZE_T_FMT "u" |
||
1548 |
typedef struct stat cs_stat_t; |
||
1549 |
#define INT64_FMT "lld" |
||
1550 |
#define INT64_X_FMT "llx" |
||
1551 |
#define __cdecl |
||
1552 |
|||
1553 |
#define MG_LWIP 1 |
||
1554 |
|||
1555 |
#define MG_NET_IF MG_NET_IF_LWIP_LOW_LEVEL |
||
1556 |
|||
1557 |
/* |
||
1558 |
* LPCXpress comes with 3 C library implementations: Newlib, NewlibNano and |
||
1559 |
*Redlib. |
||
1560 |
* See https://community.nxp.com/message/630860 for more details. |
||
1561 |
* |
||
1562 |
* Redlib is the default and lacks certain things, so we provide them. |
||
1563 |
*/ |
||
1564 |
#ifdef __REDLIB_INTERFACE_VERSION__ |
||
1565 |
|||
1566 |
/* Let LWIP define timeval for us. */ |
||
1567 |
#define LWIP_TIMEVAL_PRIVATE 1 |
||
1568 |
|||
1569 |
#define va_copy(d, s) __builtin_va_copy(d, s) |
||
1570 |
|||
1571 |
#define CS_ENABLE_TO64 1 |
||
1572 |
#define to64(x) cs_to64(x) |
||
1573 |
|||
1574 |
#define CS_ENABLE_STRDUP 1 |
||
1575 |
|||
1576 |
#else |
||
1577 |
|||
1578 |
#include <sys/time.h> |
||
1579 |
#define LWIP_TIMEVAL_PRIVATE 0 |
||
1580 |
#define to64(x) strtoll(x, NULL, 10) |
||
1581 |
|||
1582 |
#endif |
||
1583 |
|||
1584 |
#endif /* CS_PLATFORM == CS_P_NXP_LPC */ |
||
1585 |
#endif /* CS_COMMON_PLATFORMS_PLATFORM_NXP_LPC_H_ */ |
||
1586 |
#ifdef MJS_MODULE_LINES |
||
1587 |
#line 1 "common/platforms/platform_nxp_kinetis.h" |
||
1588 |
#endif |
||
1589 |
/* |
||
1590 |
* Copyright (c) 2014-2018 Cesanta Software Limited |
||
1591 |
* All rights reserved |
||
1592 |
* |
||
1593 |
* Licensed under the Apache License, Version 2.0 (the ""License""); |
||
1594 |
* you may not use this file except in compliance with the License. |
||
1595 |
* You may obtain a copy of the License at |
||
1596 |
* |
||
1597 |
* http://www.apache.org/licenses/LICENSE-2.0 |
||
1598 |
* |
||
1599 |
* Unless required by applicable law or agreed to in writing, software |
||
1600 |
* distributed under the License is distributed on an ""AS IS"" BASIS, |
||
1601 |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||
1602 |
* See the License for the specific language governing permissions and |
||
1603 |
* limitations under the License. |
||
1604 |
*/ |
||
1605 |
|||
1606 |
#ifndef CS_COMMON_PLATFORMS_PLATFORM_NXP_KINETIS_H_ |
||
1607 |
#define CS_COMMON_PLATFORMS_PLATFORM_NXP_KINETIS_H_ |
||
1608 |
|||
1609 |
#if CS_PLATFORM == CS_P_NXP_KINETIS |
||
1610 |
|||
1611 |
#include <ctype.h> |
||
1612 |
#include <inttypes.h> |
||
1613 |
#include <string.h> |
||
1614 |
#include <sys/time.h> |
||
1615 |
|||
1616 |
#define SIZE_T_FMT "u" |
||
1617 |
typedef struct stat cs_stat_t; |
||
1618 |
#define to64(x) strtoll(x, NULL, 10) |
||
1619 |
#define INT64_FMT "lld" |
||
1620 |
#define INT64_X_FMT "llx" |
||
1621 |
#define __cdecl |
||
1622 |
|||
1623 |
#define MG_LWIP 1 |
||
1624 |
|||
1625 |
#define MG_NET_IF MG_NET_IF_LWIP_LOW_LEVEL |
||
1626 |
|||
1627 |
/* struct timeval is defined in sys/time.h. */ |
||
1628 |
#define LWIP_TIMEVAL_PRIVATE 0 |
||
1629 |
|||
1630 |
#endif /* CS_PLATFORM == CS_P_NXP_KINETIS */ |
||
1631 |
#endif /* CS_COMMON_PLATFORMS_PLATFORM_NXP_KINETIS_H_ */ |
||
1632 |
#ifdef MJS_MODULE_LINES |
||
1633 |
#line 1 "common/platforms/platform_pic32.h" |
||
1634 |
#endif |
||
1635 |
/* |
||
1636 |
* Copyright (c) 2014-2018 Cesanta Software Limited |
||
1637 |
* All rights reserved |
||
1638 |
* |
||
1639 |
* Licensed under the Apache License, Version 2.0 (the ""License""); |
||
1640 |
* you may not use this file except in compliance with the License. |
||
1641 |
* You may obtain a copy of the License at |
||
1642 |
* |
||
1643 |
* http://www.apache.org/licenses/LICENSE-2.0 |
||
1644 |
* |
||
1645 |
* Unless required by applicable law or agreed to in writing, software |
||
1646 |
* distributed under the License is distributed on an ""AS IS"" BASIS, |
||
1647 |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||
1648 |
* See the License for the specific language governing permissions and |
||
1649 |
* limitations under the License. |
||
1650 |
*/ |
||
1651 |
|||
1652 |
#ifndef CS_COMMON_PLATFORMS_PLATFORM_PIC32_H_ |
||
1653 |
#define CS_COMMON_PLATFORMS_PLATFORM_PIC32_H_ |
||
1654 |
|||
1655 |
#if CS_PLATFORM == CS_P_PIC32 |
||
1656 |
|||
1657 |
#define MG_NET_IF MG_NET_IF_PIC32 |
||
1658 |
|||
1659 |
#include <stdint.h> |
||
1660 |
#include <time.h> |
||
1661 |
#include <ctype.h> |
||
1662 |
#include <stdlib.h> |
||
1663 |
|||
1664 |
#include <system_config.h> |
||
1665 |
#include <system_definitions.h> |
||
1666 |
|||
1667 |
#include <sys/types.h> |
||
1668 |
|||
1669 |
typedef TCP_SOCKET sock_t; |
||
1670 |
#define to64(x) strtoll(x, NULL, 10) |
||
1671 |
|||
1672 |
#define SIZE_T_FMT "lu" |
||
1673 |
#define INT64_FMT "lld" |
||
1674 |
|||
1675 |
#ifndef CS_ENABLE_STDIO |
||
1676 |
#define CS_ENABLE_STDIO 1 |
||
1677 |
#endif |
||
1678 |
|||
1679 |
char *inet_ntoa(struct in_addr in); |
||
1680 |
|||
1681 |
#endif /* CS_PLATFORM == CS_P_PIC32 */ |
||
1682 |
|||
1683 |
#endif /* CS_COMMON_PLATFORMS_PLATFORM_PIC32_H_ */ |
||
1684 |
#ifdef MJS_MODULE_LINES |
||
1685 |
#line 1 "common/platforms/platform_stm32.h" |
||
1686 |
#endif |
||
1687 |
/* |
||
1688 |
* Copyright (c) 2014-2018 Cesanta Software Limited |
||
1689 |
* All rights reserved |
||
1690 |
* |
||
1691 |
* Licensed under the Apache License, Version 2.0 (the ""License""); |
||
1692 |
* you may not use this file except in compliance with the License. |
||
1693 |
* You may obtain a copy of the License at |
||
1694 |
* |
||
1695 |
* http://www.apache.org/licenses/LICENSE-2.0 |
||
1696 |
* |
||
1697 |
* Unless required by applicable law or agreed to in writing, software |
||
1698 |
* distributed under the License is distributed on an ""AS IS"" BASIS, |
||
1699 |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||
1700 |
* See the License for the specific language governing permissions and |
||
1701 |
* limitations under the License. |
||
1702 |
*/ |
||
1703 |
|||
1704 |
#ifndef CS_COMMON_PLATFORMS_PLATFORM_STM32_H_ |
||
1705 |
#define CS_COMMON_PLATFORMS_PLATFORM_STM32_H_ |
||
1706 |
#if CS_PLATFORM == CS_P_STM32 |
||
1707 |
|||
1708 |
#include <ctype.h> |
||
1709 |
#include <errno.h> |
||
1710 |
#include <fcntl.h> |
||
1711 |
#include <stdint.h> |
||
1712 |
#include <stdio.h> |
||
1713 |
#include <string.h> |
||
1714 |
#include <sys/stat.h> |
||
1715 |
#include <sys/time.h> |
||
1716 |
#include <sys/types.h> |
||
1717 |
#include <unistd.h> |
||
1718 |
#include <dirent.h> |
||
1719 |
|||
1720 |
#include <stm32_sdk_hal.h> |
||
1721 |
|||
1722 |
#define to64(x) strtoll(x, NULL, 10) |
||
1723 |
#define INT64_FMT PRId64 |
||
1724 |
#define SIZE_T_FMT "u" |
||
1725 |
typedef struct stat cs_stat_t; |
||
1726 |
#define DIRSEP '/' |
||
1727 |
|||
1728 |
#ifndef CS_ENABLE_STDIO |
||
1729 |
#define CS_ENABLE_STDIO 1 |
||
1730 |
#endif |
||
1731 |
|||
1732 |
#ifndef MG_ENABLE_FILESYSTEM |
||
1733 |
#define MG_ENABLE_FILESYSTEM 1 |
||
1734 |
#endif |
||
1735 |
|||
1736 |
#endif /* CS_PLATFORM == CS_P_STM32 */ |
||
1737 |
#endif /* CS_COMMON_PLATFORMS_PLATFORM_STM32_H_ */ |
||
1738 |
#ifdef MJS_MODULE_LINES |
||
1739 |
#line 1 "common/cs_dbg.h" |
||
1740 |
#endif |
||
1741 |
/* |
||
1742 |
* Copyright (c) 2014-2018 Cesanta Software Limited |
||
1743 |
* All rights reserved |
||
1744 |
* |
||
1745 |
* Licensed under the Apache License, Version 2.0 (the ""License""); |
||
1746 |
* you may not use this file except in compliance with the License. |
||
1747 |
* You may obtain a copy of the License at |
||
1748 |
* |
||
1749 |
* http://www.apache.org/licenses/LICENSE-2.0 |
||
1750 |
* |
||
1751 |
* Unless required by applicable law or agreed to in writing, software |
||
1752 |
* distributed under the License is distributed on an ""AS IS"" BASIS, |
||
1753 |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||
1754 |
* See the License for the specific language governing permissions and |
||
1755 |
* limitations under the License. |
||
1756 |
*/ |
||
1757 |
|||
1758 |
#ifndef CS_COMMON_CS_DBG_H_ |
||
1759 |
#define CS_COMMON_CS_DBG_H_ |
||
1760 |
|||
1761 |
/* Amalgamated: #include "common/platform.h" */ |
||
1762 |
|||
1763 |
#if CS_ENABLE_STDIO |
||
1764 |
#include <stdio.h> |
||
1765 |
#endif |
||
1766 |
|||
1767 |
#ifndef CS_ENABLE_DEBUG |
||
1768 |
#define CS_ENABLE_DEBUG 0 |
||
1769 |
#endif |
||
1770 |
|||
1771 |
#ifndef CS_LOG_ENABLE_TS_DIFF |
||
1772 |
#define CS_LOG_ENABLE_TS_DIFF 0 |
||
1773 |
#endif |
||
1774 |
|||
1775 |
#ifdef __cplusplus |
||
1776 |
extern "C" { |
||
1777 |
#endif /* __cplusplus */ |
||
1778 |
|||
1779 |
/* |
||
1780 |
* Log level; `LL_INFO` is the default. Use `cs_log_set_level()` to change it. |
||
1781 |
*/ |
||
1782 |
enum cs_log_level { |
||
1783 |
LL_NONE = -1, |
||
1784 |
LL_ERROR = 0, |
||
1785 |
LL_WARN = 1, |
||
1786 |
LL_INFO = 2, |
||
1787 |
LL_DEBUG = 3, |
||
1788 |
LL_VERBOSE_DEBUG = 4, |
||
1789 |
|||
1790 |
_LL_MIN = -2, |
||
1791 |
_LL_MAX = 5, |
||
1792 |
}; |
||
1793 |
|||
1794 |
/* |
||
1795 |
* Set max log level to print; messages with the level above the given one will |
||
1796 |
* not be printed. |
||
1797 |
*/ |
||
1798 |
void cs_log_set_level(enum cs_log_level level); |
||
1799 |
|||
1800 |
/* |
||
1801 |
* Set log filter. NULL (a default) logs everything. |
||
1802 |
* Otherwise, function name and file name will be tested against the given |
||
1803 |
* pattern, and only matching messages will be printed. |
||
1804 |
* |
||
1805 |
* For the pattern syntax, refer to `mg_match_prefix()` in `str_util.h`. |
||
1806 |
* |
||
1807 |
* Example: |
||
1808 |
* ```c |
||
1809 |
* void foo(void) { |
||
1810 |
* LOG(LL_INFO, ("hello from foo")); |
||
1811 |
* } |
||
1812 |
* |
||
1813 |
* void bar(void) { |
||
1814 |
* LOG(LL_INFO, ("hello from bar")); |
||
1815 |
* } |
||
1816 |
* |
||
1817 |
* void test(void) { |
||
1818 |
* cs_log_set_filter(NULL); |
||
1819 |
* foo(); |
||
1820 |
* bar(); |
||
1821 |
* |
||
1822 |
* cs_log_set_filter("f*"); |
||
1823 |
* foo(); |
||
1824 |
* bar(); // Will NOT print anything |
||
1825 |
* |
||
1826 |
* cs_log_set_filter("bar"); |
||
1827 |
* foo(); // Will NOT print anything |
||
1828 |
* bar(); |
||
1829 |
* } |
||
1830 |
* ``` |
||
1831 |
*/ |
||
1832 |
void cs_log_set_filter(const char *pattern); |
||
1833 |
|||
1834 |
/* |
||
1835 |
* Helper function which prints message prefix with the given `level`, function |
||
1836 |
* name `func` and `filename`. If message should be printed (accordingly to the |
||
1837 |
* current log level and filter), prints the prefix and returns 1, otherwise |
||
1838 |
* returns 0. |
||
1839 |
* |
||
1840 |
* Clients should typically just use `LOG()` macro. |
||
1841 |
*/ |
||
1842 |
int cs_log_print_prefix(enum cs_log_level level, const char *func, |
||
1843 |
const char *filename); |
||
1844 |
|||
1845 |
extern enum cs_log_level cs_log_threshold; |
||
1846 |
|||
1847 |
#if CS_ENABLE_STDIO |
||
1848 |
|||
1849 |
/* |
||
1850 |
* Set file to write logs into. If `NULL`, logs go to `stderr`. |
||
1851 |
*/ |
||
1852 |
void cs_log_set_file(FILE *file); |
||
1853 |
|||
1854 |
/* |
||
1855 |
* Prints log to the current log file, appends "\n" in the end and flushes the |
||
1856 |
* stream. |
||
1857 |
*/ |
||
1858 |
void cs_log_printf(const char *fmt, ...) PRINTF_LIKE(1, 2); |
||
1859 |
|||
1860 |
/* |
||
1861 |
* Format and print message `x` with the given level `l`. Example: |
||
1862 |
* |
||
1863 |
* ```c |
||
1864 |
* LOG(LL_INFO, ("my info message: %d", 123)); |
||
1865 |
* LOG(LL_DEBUG, ("my debug message: %d", 123)); |
||
1866 |
* ``` |
||
1867 |
*/ |
||
1868 |
#define LOG(l, x) \ |
||
1869 |
do { \ |
||
1870 |
if (cs_log_print_prefix(l, __func__, __FILE__)) cs_log_printf x; \ |
||
1871 |
} while (0) |
||
1872 |
|||
1873 |
#ifndef CS_NDEBUG |
||
1874 |
|||
1875 |
/* |
||
1876 |
* Shortcut for `LOG(LL_VERBOSE_DEBUG, (...))` |
||
1877 |
*/ |
||
1878 |
#define DBG(x) LOG(LL_VERBOSE_DEBUG, x) |
||
1879 |
|||
1880 |
#else /* NDEBUG */ |
||
1881 |
|||
1882 |
#define DBG(x) |
||
1883 |
|||
1884 |
#endif |
||
1885 |
|||
1886 |
#else /* CS_ENABLE_STDIO */ |
||
1887 |
|||
1888 |
#define LOG(l, x) |
||
1889 |
#define DBG(x) |
||
1890 |
|||
1891 |
#endif |
||
1892 |
|||
1893 |
#ifdef __cplusplus |
||
1894 |
} |
||
1895 |
#endif /* __cplusplus */ |
||
1896 |
|||
1897 |
#endif /* CS_COMMON_CS_DBG_H_ */ |
||
1898 |
#ifdef MJS_MODULE_LINES |
||
1899 |
#line 1 "common/cs_time.h" |
||
1900 |
#endif |
||
1901 |
/* |
||
1902 |
* Copyright (c) 2014-2018 Cesanta Software Limited |
||
1903 |
* All rights reserved |
||
1904 |
* |
||
1905 |
* Licensed under the Apache License, Version 2.0 (the ""License""); |
||
1906 |
* you may not use this file except in compliance with the License. |
||
1907 |
* You may obtain a copy of the License at |
||
1908 |
* |
||
1909 |
* http://www.apache.org/licenses/LICENSE-2.0 |
||
1910 |
* |
||
1911 |
* Unless required by applicable law or agreed to in writing, software |
||
1912 |
* distributed under the License is distributed on an ""AS IS"" BASIS, |
||
1913 |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||
1914 |
* See the License for the specific language governing permissions and |
||
1915 |
* limitations under the License. |
||
1916 |
*/ |
||
1917 |
|||
1918 |
#ifndef CS_COMMON_CS_TIME_H_ |
||
1919 |
#define CS_COMMON_CS_TIME_H_ |
||
1920 |
|||
1921 |
#include <time.h> |
||
1922 |
|||
1923 |
/* Amalgamated: #include "common/platform.h" */ |
||
1924 |
|||
1925 |
#ifdef __cplusplus |
||
1926 |
extern "C" { |
||
1927 |
#endif /* __cplusplus */ |
||
1928 |
|||
1929 |
/* Sub-second granularity time(). */ |
||
1930 |
double cs_time(void); |
||
1931 |
|||
1932 |
/* |
||
1933 |
* Similar to (non-standard) timegm, converts broken-down time into the number |
||
1934 |
* of seconds since Unix Epoch. |
||
1935 |
*/ |
||
1936 |
double cs_timegm(const struct tm *tm); |
||
1937 |
|||
1938 |
#ifdef __cplusplus |
||
1939 |
} |
||
1940 |
#endif /* __cplusplus */ |
||
1941 |
|||
1942 |
#endif /* CS_COMMON_CS_TIME_H_ */ |
||
1943 |
#ifdef MJS_MODULE_LINES |
||
1944 |
#line 1 "common/mg_str.h" |
||
1945 |
#endif |
||
1946 |
/* |
||
1947 |
* Copyright (c) 2014-2018 Cesanta Software Limited |
||
1948 |
* All rights reserved |
||
1949 |
* |
||
1950 |
* Licensed under the Apache License, Version 2.0 (the ""License""); |
||
1951 |
* you may not use this file except in compliance with the License. |
||
1952 |
* You may obtain a copy of the License at |
||
1953 |
* |
||
1954 |
* http://www.apache.org/licenses/LICENSE-2.0 |
||
1955 |
* |
||
1956 |
* Unless required by applicable law or agreed to in writing, software |
||
1957 |
* distributed under the License is distributed on an ""AS IS"" BASIS, |
||
1958 |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||
1959 |
* See the License for the specific language governing permissions and |
||
1960 |
* limitations under the License. |
||
1961 |
*/ |
||
1962 |
|||
1963 |
#ifndef CS_COMMON_MG_STR_H_ |
||
1964 |
#define CS_COMMON_MG_STR_H_ |
||
1965 |
|||
1966 |
#include <stddef.h> |
||
1967 |
|||
1968 |
#ifdef __cplusplus |
||
1969 |
extern "C" { |
||
1970 |
#endif |
||
1971 |
|||
1972 |
/* Describes chunk of memory */ |
||
1973 |
struct mg_str { |
||
1974 |
const char *p; /* Memory chunk pointer */ |
||
1975 |
size_t len; /* Memory chunk length */ |
||
1976 |
}; |
||
1977 |
|||
1978 |
/* |
||
1979 |
* Helper function for creating mg_str struct from plain C string. |
||
1980 |
* `NULL` is allowed and becomes `{NULL, 0}`. |
||
1981 |
*/ |
||
1982 |
struct mg_str mg_mk_str(const char *s); |
||
1983 |
|||
1984 |
/* |
||
1985 |
* Like `mg_mk_str`, but takes string length explicitly. |
||
1986 |
*/ |
||
1987 |
struct mg_str mg_mk_str_n(const char *s, size_t len); |
||
1988 |
|||
1989 |
/* Macro for initializing mg_str. */ |
||
1990 |
#define MG_MK_STR(str_literal) \ |
||
1991 |
{ str_literal, sizeof(str_literal) - 1 } |
||
1992 |
#define MG_NULL_STR \ |
||
1993 |
{ NULL, 0 } |
||
1994 |
|||
1995 |
/* |
||
1996 |
* Cross-platform version of `strcmp()` where where first string is |
||
1997 |
* specified by `struct mg_str`. |
||
1998 |
*/ |
||
1999 |
int mg_vcmp(const struct mg_str *str2, const char *str1); |
||
2000 |
|||
2001 |
/* |
||
2002 |
* Cross-platform version of `strncasecmp()` where first string is |
||
2003 |
* specified by `struct mg_str`. |
||
2004 |
*/ |
||
2005 |
int mg_vcasecmp(const struct mg_str *str2, const char *str1); |
||
2006 |
|||
2007 |
/* Creates a copy of s (heap-allocated). */ |
||
2008 |
struct mg_str mg_strdup(const struct mg_str s); |
||
2009 |
|||
2010 |
/* |
||
2011 |
* Creates a copy of s (heap-allocated). |
||
2012 |
* Resulting string is NUL-terminated (but NUL is not included in len). |
||
2013 |
*/ |
||
2014 |
struct mg_str mg_strdup_nul(const struct mg_str s); |
||
2015 |
|||
2016 |
/* |
||
2017 |
* Locates character in a string. |
||
2018 |
*/ |
||
2019 |
const char *mg_strchr(const struct mg_str s, int c); |
||
2020 |
|||
2021 |
/* |
||
2022 |
* Compare two `mg_str`s; return value is the same as `strcmp`. |
||
2023 |
*/ |
||
2024 |
int mg_strcmp(const struct mg_str str1, const struct mg_str str2); |
||
2025 |
|||
2026 |
/* |
||
2027 |
* Like `mg_strcmp`, but compares at most `n` characters. |
||
2028 |
*/ |
||
2029 |
int mg_strncmp(const struct mg_str str1, const struct mg_str str2, size_t n); |
||
2030 |
|||
2031 |
/* |
||
2032 |
* Finds the first occurrence of a substring `needle` in the `haystack`. |
||
2033 |
*/ |
||
2034 |
const char *mg_strstr(const struct mg_str haystack, const struct mg_str needle); |
||
2035 |
|||
2036 |
#ifdef __cplusplus |
||
2037 |
} |
||
2038 |
#endif |
||
2039 |
|||
2040 |
#endif /* CS_COMMON_MG_STR_H_ */ |
||
2041 |
#ifdef MJS_MODULE_LINES |
||
2042 |
#line 1 "common/str_util.h" |
||
2043 |
#endif |
||
2044 |
/* |
||
2045 |
* Copyright (c) 2014-2018 Cesanta Software Limited |
||
2046 |
* All rights reserved |
||
2047 |
* |
||
2048 |
* Licensed under the Apache License, Version 2.0 (the ""License""); |
||
2049 |
* you may not use this file except in compliance with the License. |
||
2050 |
* You may obtain a copy of the License at |
||
2051 |
* |
||
2052 |
* http://www.apache.org/licenses/LICENSE-2.0 |
||
2053 |
* |
||
2054 |
* Unless required by applicable law or agreed to in writing, software |
||
2055 |
* distributed under the License is distributed on an ""AS IS"" BASIS, |
||
2056 |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||
2057 |
* See the License for the specific language governing permissions and |
||
2058 |
* limitations under the License. |
||
2059 |
*/ |
||
2060 |
|||
2061 |
#ifndef CS_COMMON_STR_UTIL_H_ |
||
2062 |
#define CS_COMMON_STR_UTIL_H_ |
||
2063 |
|||
2064 |
#include <stdarg.h> |
||
2065 |
#include <stdlib.h> |
||
2066 |
|||
2067 |
/* Amalgamated: #include "common/mg_str.h" */ |
||
2068 |
/* Amalgamated: #include "common/platform.h" */ |
||
2069 |
|||
2070 |
#ifndef CS_ENABLE_STRDUP |
||
2071 |
#define CS_ENABLE_STRDUP 0 |
||
2072 |
#endif |
||
2073 |
|||
2074 |
#ifndef CS_ENABLE_TO64 |
||
2075 |
#define CS_ENABLE_TO64 0 |
||
2076 |
#endif |
||
2077 |
|||
2078 |
/* |
||
2079 |
* Expands to a string representation of its argument: e.g. |
||
2080 |
* `CS_STRINGIFY_LIT(5) expands to "5"` |
||
2081 |
*/ |
||
2082 |
#define CS_STRINGIFY_LIT(x) #x |
||
2083 |
|||
2084 |
/* |
||
2085 |
* Expands to a string representation of its argument, which is allowed |
||
2086 |
* to be a macro: e.g. |
||
2087 |
* |
||
2088 |
* #define FOO 123 |
||
2089 |
* CS_STRINGIFY_MACRO(FOO) |
||
2090 |
* |
||
2091 |
* expands to 123. |
||
2092 |
*/ |
||
2093 |
#define CS_STRINGIFY_MACRO(x) CS_STRINGIFY_LIT(x) |
||
2094 |
|||
2095 |
#ifdef __cplusplus |
||
2096 |
extern "C" { |
||
2097 |
#endif |
||
2098 |
|||
2099 |
/* |
||
2100 |
* Equivalent of standard `strnlen()`. |
||
2101 |
*/ |
||
2102 |
size_t c_strnlen(const char *s, size_t maxlen); |
||
2103 |
|||
2104 |
/* |
||
2105 |
* Equivalent of standard `snprintf()`. |
||
2106 |
*/ |
||
2107 |
int c_snprintf(char *buf, size_t buf_size, const char *format, ...) |
||
2108 |
PRINTF_LIKE(3, 4); |
||
2109 |
|||
2110 |
/* |
||
2111 |
* Equivalent of standard `vsnprintf()`. |
||
2112 |
*/ |
||
2113 |
int c_vsnprintf(char *buf, size_t buf_size, const char *format, va_list ap); |
||
2114 |
|||
2115 |
/* |
||
2116 |
* Find the first occurrence of find in s, where the search is limited to the |
||
2117 |
* first slen characters of s. |
||
2118 |
*/ |
||
2119 |
const char *c_strnstr(const char *s, const char *find, size_t slen); |
||
2120 |
|||
2121 |
/* |
||
2122 |
* Stringify binary data. Output buffer size must be 2 * size_of_input + 1 |
||
2123 |
* because each byte of input takes 2 bytes in string representation |
||
2124 |
* plus 1 byte for the terminating \0 character. |
||
2125 |
*/ |
||
2126 |
void cs_to_hex(char *to, const unsigned char *p, size_t len); |
||
2127 |
|||
2128 |
/* |
||
2129 |
* Convert stringified binary data back to binary. |
||
2130 |
* Does the reverse of `cs_to_hex()`. |
||
2131 |
*/ |
||
2132 |
void cs_from_hex(char *to, const char *p, size_t len); |
||
2133 |
|||
2134 |
#if CS_ENABLE_STRDUP |
||
2135 |
/* |
||
2136 |
* Equivalent of standard `strdup()`, defined if only `CS_ENABLE_STRDUP` is 1. |
||
2137 |
*/ |
||
2138 |
char *strdup(const char *src); |
||
2139 |
#endif |
||
2140 |
|||
2141 |
#if CS_ENABLE_TO64 |
||
2142 |
#include <stdint.h> |
||
2143 |
/* |
||
2144 |
* Simple string -> int64 conversion routine. |
||
2145 |
*/ |
||
2146 |
int64_t cs_to64(const char *s); |
||
2147 |
#endif |
||
2148 |
|||
2149 |
/* |
||
2150 |
* Cross-platform version of `strncasecmp()`. |
||
2151 |
*/ |
||
2152 |
int mg_ncasecmp(const char *s1, const char *s2, size_t len); |
||
2153 |
|||
2154 |
/* |
||
2155 |
* Cross-platform version of `strcasecmp()`. |
||
2156 |
*/ |
||
2157 |
int mg_casecmp(const char *s1, const char *s2); |
||
2158 |
|||
2159 |
/* |
||
2160 |
* Prints message to the buffer. If the buffer is large enough to hold the |
||
2161 |
* message, it returns buffer. If buffer is to small, it allocates a large |
||
2162 |
* enough buffer on heap and returns allocated buffer. |
||
2163 |
* This is a supposed use case: |
||
2164 |
* |
||
2165 |
* ```c |
||
2166 |
* char buf[5], *p = buf; |
||
2167 |
* mg_avprintf(&p, sizeof(buf), "%s", "hi there"); |
||
2168 |
* use_p_somehow(p); |
||
2169 |
* if (p != buf) { |
||
2170 |
* free(p); |
||
2171 |
* } |
||
2172 |
* ``` |
||
2173 |
* |
||
2174 |
* The purpose of this is to avoid malloc-ing if generated strings are small. |
||
2175 |
*/ |
||
2176 |
int mg_asprintf(char **buf, size_t size, const char *fmt, ...) |
||
2177 |
PRINTF_LIKE(3, 4); |
||
2178 |
|||
2179 |
/* Same as mg_asprintf, but takes varargs list. */ |
||
2180 |
int mg_avprintf(char **buf, size_t size, const char *fmt, va_list ap); |
||
2181 |
|||
2182 |
/* |
||
2183 |
* A helper function for traversing a comma separated list of values. |
||
2184 |
* It returns a list pointer shifted to the next value or NULL if the end |
||
2185 |
* of the list found. |
||
2186 |
* The value is stored in a val vector. If the value has a form "x=y", then |
||
2187 |
* eq_val vector is initialised to point to the "y" part, and val vector length |
||
2188 |
* is adjusted to point only to "x". |
||
2189 |
* If the list is just a comma separated list of entries, like "aa,bb,cc" then |
||
2190 |
* `eq_val` will contain zero-length string. |
||
2191 |
* |
||
2192 |
* The purpose of this function is to parse comma separated string without |
||
2193 |
* any copying/memory allocation. |
||
2194 |
*/ |
||
2195 |
const char *mg_next_comma_list_entry(const char *list, struct mg_str *val, |
||
2196 |
struct mg_str *eq_val); |
||
2197 |
|||
2198 |
/* |
||
2199 |
* Like `mg_next_comma_list_entry()`, but takes `list` as `struct mg_str`. |
||
2200 |
*/ |
||
2201 |
struct mg_str mg_next_comma_list_entry_n(struct mg_str list, struct mg_str *val, |
||
2202 |
struct mg_str *eq_val); |
||
2203 |
|||
2204 |
/* |
||
2205 |
* Matches 0-terminated string (mg_match_prefix) or string with given length |
||
2206 |
* mg_match_prefix_n against a glob pattern. Glob syntax: |
||
2207 |
* ``` |
||
2208 |
* - * matches zero or more characters until a slash character / |
||
2209 |
* - ** matches zero or more characters |
||
2210 |
* - ? Matches exactly one character which is not a slash / |
||
2211 |
* - | or , divides alternative patterns |
||
2212 |
* - any other character matches itself |
||
2213 |
* ``` |
||
2214 |
* Match is case-insensitive. Return number of bytes matched. |
||
2215 |
* Examples: |
||
2216 |
* ``` |
||
2217 |
* mg_match_prefix("a*f", len, "abcdefgh") == 6 |
||
2218 |
* mg_match_prefix("a*f", len, "abcdexgh") == 0 |
||
2219 |
* mg_match_prefix("a*f|de*,xy", len, "defgh") == 5 |
||
2220 |
* mg_match_prefix("?*", len, "abc") == 3 |
||
2221 |
* mg_match_prefix("?*", len, "") == 0 |
||
2222 |
* ``` |
||
2223 |
*/ |
||
2224 |
size_t mg_match_prefix(const char *pattern, int pattern_len, const char *str); |
||
2225 |
|||
2226 |
/* |
||
2227 |
* Like `mg_match_prefix()`, but takes `pattern` and `str` as `struct mg_str`. |
||
2228 |
*/ |
||
2229 |
size_t mg_match_prefix_n(const struct mg_str pattern, const struct mg_str str); |
||
2230 |
|||
2231 |
#ifdef __cplusplus |
||
2232 |
} |
||
2233 |
#endif |
||
2234 |
|||
2235 |
#endif /* CS_COMMON_STR_UTIL_H_ */ |
||
2236 |
#ifdef MJS_MODULE_LINES |
||
2237 |
#line 1 "common/cs_file.h" |
||
2238 |
#endif |
||
2239 |
/* |
||
2240 |
* Copyright (c) 2014-2018 Cesanta Software Limited |
||
2241 |
* All rights reserved |
||
2242 |
* |
||
2243 |
* Licensed under the Apache License, Version 2.0 (the ""License""); |
||
2244 |
* you may not use this file except in compliance with the License. |
||
2245 |
* You may obtain a copy of the License at |
||
2246 |
* |
||
2247 |
* http://www.apache.org/licenses/LICENSE-2.0 |
||
2248 |
* |
||
2249 |
* Unless required by applicable law or agreed to in writing, software |
||
2250 |
* distributed under the License is distributed on an ""AS IS"" BASIS, |
||
2251 |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||
2252 |
* See the License for the specific language governing permissions and |
||
2253 |
* limitations under the License. |
||
2254 |
*/ |
||
2255 |
|||
2256 |
#ifndef CS_COMMON_CS_FILE_H_ |
||
2257 |
#define CS_COMMON_CS_FILE_H_ |
||
2258 |
|||
2259 |
/* Amalgamated: #include "common/platform.h" */ |
||
2260 |
|||
2261 |
#ifdef __cplusplus |
||
2262 |
extern "C" { |
||
2263 |
#endif /* __cplusplus */ |
||
2264 |
|||
2265 |
/* |
||
2266 |
* Read whole file `path` in memory. It is responsibility of the caller |
||
2267 |
* to `free()` allocated memory. File content is guaranteed to be |
||
2268 |
* '\0'-terminated. File size is returned in `size` variable, which does not |
||
2269 |
* count terminating `\0`. |
||
2270 |
* Return: allocated memory, or NULL on error. |
||
2271 |
*/ |
||
2272 |
char *cs_read_file(const char *path, size_t *size); |
||
2273 |
|||
2274 |
#ifdef CS_MMAP |
||
2275 |
/* |
||
2276 |
* Only on platforms which support mmapping: mmap file `path` to the returned |
||
2277 |
* address. File size is written to `*size`. |
||
2278 |
*/ |
||
2279 |
char *cs_mmap_file(const char *path, size_t *size); |
||
2280 |
#endif |
||
2281 |
|||
2282 |
#ifdef __cplusplus |
||
2283 |
} |
||
2284 |
#endif /* __cplusplus */ |
||
2285 |
|||
2286 |
#endif /* CS_COMMON_CS_FILE_H_ */ |
||
2287 |
#ifdef MJS_MODULE_LINES |
||
2288 |
#line 1 "common/mbuf.h" |
||
2289 |
#endif |
||
2290 |
/* |
||
2291 |
* Copyright (c) 2014-2018 Cesanta Software Limited |
||
2292 |
* All rights reserved |
||
2293 |
* |
||
2294 |
* Licensed under the Apache License, Version 2.0 (the ""License""); |
||
2295 |
* you may not use this file except in compliance with the License. |
||
2296 |
* You may obtain a copy of the License at |
||
2297 |
* |
||
2298 |
* http://www.apache.org/licenses/LICENSE-2.0 |
||
2299 |
* |
||
2300 |
* Unless required by applicable law or agreed to in writing, software |
||
2301 |
* distributed under the License is distributed on an ""AS IS"" BASIS, |
||
2302 |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||
2303 |
* See the License for the specific language governing permissions and |
||
2304 |
* limitations under the License. |
||
2305 |
*/ |
||
2306 |
|||
2307 |
/* |
||
2308 |
* === Memory Buffers |
||
2309 |
* |
||
2310 |
* Mbufs are mutable/growing memory buffers, like C++ strings. |
||
2311 |
* Mbuf can append data to the end of a buffer or insert data into arbitrary |
||
2312 |
* position in the middle of a buffer. The buffer grows automatically when |
||
2313 |
* needed. |
||
2314 |
*/ |
||
2315 |
|||
2316 |
#ifndef CS_COMMON_MBUF_H_ |
||
2317 |
#define CS_COMMON_MBUF_H_ |
||
2318 |
|||
2319 |
#include <stdlib.h> |
||
2320 |
/* Amalgamated: #include "common/platform.h" */ |
||
2321 |
|||
2322 |
#if defined(__cplusplus) |
||
2323 |
extern "C" { |
||
2324 |
#endif |
||
2325 |
|||
2326 |
#ifndef MBUF_SIZE_MULTIPLIER |
||
2327 |
#define MBUF_SIZE_MULTIPLIER 1.5 |
||
2328 |
#endif |
||
2329 |
|||
2330 |
#ifndef MBUF_SIZE_MAX_HEADROOM |
||
2331 |
#ifdef BUFSIZ |
||
2332 |
#define MBUF_SIZE_MAX_HEADROOM BUFSIZ |
||
2333 |
#else |
||
2334 |
#define MBUF_SIZE_MAX_HEADROOM 1024 |
||
2335 |
#endif |
||
2336 |
#endif |
||
2337 |
|||
2338 |
/* Memory buffer descriptor */ |
||
2339 |
struct mbuf { |
||
2340 |
char *buf; /* Buffer pointer */ |
||
2341 |
size_t len; /* Data length. Data is located between offset 0 and len. */ |
||
2342 |
size_t size; /* Buffer size allocated by realloc(1). Must be >= len */ |
||
2343 |
}; |
||
2344 |
|||
2345 |
/* |
||
2346 |
* Initialises an Mbuf. |
||
2347 |
* `initial_capacity` specifies the initial capacity of the mbuf. |
||
2348 |
*/ |
||
2349 |
void mbuf_init(struct mbuf *, size_t initial_capacity); |
||
2350 |
|||
2351 |
/* Frees the space allocated for the mbuffer and resets the mbuf structure. */ |
||
2352 |
void mbuf_free(struct mbuf *); |
||
2353 |
|||
2354 |
/* |
||
2355 |
* Appends data to the Mbuf. |
||
2356 |
* |
||
2357 |
* Returns the number of bytes appended or 0 if out of memory. |
||
2358 |
*/ |
||
2359 |
size_t mbuf_append(struct mbuf *, const void *data, size_t data_size); |
||
2360 |
|||
2361 |
/* |
||
2362 |
* Inserts data at a specified offset in the Mbuf. |
||
2363 |
* |
||
2364 |
* Existing data will be shifted forwards and the buffer will |
||
2365 |
* be grown if necessary. |
||
2366 |
* Returns the number of bytes inserted. |
||
2367 |
*/ |
||
2368 |
size_t mbuf_insert(struct mbuf *, size_t, const void *, size_t); |
||
2369 |
|||
2370 |
/* Removes `data_size` bytes from the beginning of the buffer. */ |
||
2371 |
void mbuf_remove(struct mbuf *, size_t data_size); |
||
2372 |
|||
2373 |
/* |
||
2374 |
* Resizes an Mbuf. |
||
2375 |
* |
||
2376 |
* If `new_size` is smaller than buffer's `len`, the |
||
2377 |
* resize is not performed. |
||
2378 |
*/ |
||
2379 |
void mbuf_resize(struct mbuf *, size_t new_size); |
||
2380 |
|||
2381 |
/* Shrinks an Mbuf by resizing its `size` to `len`. */ |
||
2382 |
void mbuf_trim(struct mbuf *); |
||
2383 |
|||
2384 |
#if defined(__cplusplus) |
||
2385 |
} |
||
2386 |
#endif /* __cplusplus */ |
||
2387 |
|||
2388 |
#endif /* CS_COMMON_MBUF_H_ */ |
||
2389 |
#ifdef MJS_MODULE_LINES |
||
2390 |
#line 1 "common/mg_mem.h" |
||
2391 |
#endif |
||
2392 |
/* |
||
2393 |
* Copyright (c) 2014-2018 Cesanta Software Limited |
||
2394 |
* All rights reserved |
||
2395 |
* |
||
2396 |
* Licensed under the Apache License, Version 2.0 (the ""License""); |
||
2397 |
* you may not use this file except in compliance with the License. |
||
2398 |
* You may obtain a copy of the License at |
||
2399 |
* |
||
2400 |
* http://www.apache.org/licenses/LICENSE-2.0 |
||
2401 |
* |
||
2402 |
* Unless required by applicable law or agreed to in writing, software |
||
2403 |
* distributed under the License is distributed on an ""AS IS"" BASIS, |
||
2404 |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||
2405 |
* See the License for the specific language governing permissions and |
||
2406 |
* limitations under the License. |
||
2407 |
*/ |
||
2408 |
|||
2409 |
#ifndef CS_COMMON_MG_MEM_H_ |
||
2410 |
#define CS_COMMON_MG_MEM_H_ |
||
2411 |
|||
2412 |
#ifdef __cplusplus |
||
2413 |
extern "C" { |
||
2414 |
#endif |
||
2415 |
|||
2416 |
#ifndef MG_MALLOC |
||
2417 |
#define MG_MALLOC malloc |
||
2418 |
#endif |
||
2419 |
|||
2420 |
#ifndef MG_CALLOC |
||
2421 |
#define MG_CALLOC calloc |
||
2422 |
#endif |
||
2423 |
|||
2424 |
#ifndef MG_REALLOC |
||
2425 |
#define MG_REALLOC realloc |
||
2426 |
#endif |
||
2427 |
|||
2428 |
#ifndef MG_FREE |
||
2429 |
#define MG_FREE free |
||
2430 |
#endif |
||
2431 |
|||
2432 |
#ifdef __cplusplus |
||
2433 |
} |
||
2434 |
#endif |
||
2435 |
|||
2436 |
#endif /* CS_COMMON_MG_MEM_H_ */ |
||
2437 |
#ifdef MJS_MODULE_LINES |
||
2438 |
#line 1 "frozen/frozen.h" |
||
2439 |
#endif |
||
2440 |
/* |
||
2441 |
* Copyright (c) 2004-2013 Sergey Lyubka <valenok@gmail.com> |
||
2442 |
* Copyright (c) 2018 Cesanta Software Limited |
||
2443 |
* All rights reserved |
||
2444 |
* |
||
2445 |
* Licensed under the Apache License, Version 2.0 (the ""License""); |
||
2446 |
* you may not use this file except in compliance with the License. |
||
2447 |
* You may obtain a copy of the License at |
||
2448 |
* |
||
2449 |
* http://www.apache.org/licenses/LICENSE-2.0 |
||
2450 |
* |
||
2451 |
* Unless required by applicable law or agreed to in writing, software |
||
2452 |
* distributed under the License is distributed on an ""AS IS"" BASIS, |
||
2453 |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||
2454 |
* See the License for the specific language governing permissions and |
||
2455 |
* limitations under the License. |
||
2456 |
*/ |
||
2457 |
|||
2458 |
#ifndef CS_FROZEN_FROZEN_H_ |
||
2459 |
#define CS_FROZEN_FROZEN_H_ |
||
2460 |
|||
2461 |
#ifdef __cplusplus |
||
2462 |
extern "C" { |
||
2463 |
#endif /* __cplusplus */ |
||
2464 |
|||
2465 |
#include <stdarg.h> |
||
2466 |
#include <stddef.h> |
||
2467 |
#include <stdio.h> |
||
2468 |
|||
2469 |
#if defined(_WIN32) && _MSC_VER < 1700 |
||
2470 |
typedef int bool; |
||
2471 |
enum { false = 0, true = 1 }; |
||
2472 |
#else |
||
2473 |
#include <stdbool.h> |
||
2474 |
#endif |
||
2475 |
|||
2476 |
/* JSON token type */ |
||
2477 |
enum json_token_type { |
||
2478 |
JSON_TYPE_INVALID = 0, /* memsetting to 0 should create INVALID value */ |
||
2479 |
JSON_TYPE_STRING, |
||
2480 |
JSON_TYPE_NUMBER, |
||
2481 |
JSON_TYPE_TRUE, |
||
2482 |
JSON_TYPE_FALSE, |
||
2483 |
JSON_TYPE_NULL, |
||
2484 |
JSON_TYPE_OBJECT_START, |
||
2485 |
JSON_TYPE_OBJECT_END, |
||
2486 |
JSON_TYPE_ARRAY_START, |
||
2487 |
JSON_TYPE_ARRAY_END, |
||
2488 |
|||
2489 |
JSON_TYPES_CNT |
||
2490 |
}; |
||
2491 |
|||
2492 |
/* |
||
2493 |
* Structure containing token type and value. Used in `json_walk()` and |
||
2494 |
* `json_scanf()` with the format specifier `%T`. |
||
2495 |
*/ |
||
2496 |
struct json_token { |
||
2497 |
const char *ptr; /* Points to the beginning of the value */ |
||
2498 |
int len; /* Value length */ |
||
2499 |
enum json_token_type type; /* Type of the token, possible values are above */ |
||
2500 |
}; |
||
2501 |
|||
2502 |
#define JSON_INVALID_TOKEN \ |
||
2503 |
{ 0, 0, JSON_TYPE_INVALID } |
||
2504 |
|||
2505 |
/* Error codes */ |
||
2506 |
#define JSON_STRING_INVALID -1 |
||
2507 |
#define JSON_STRING_INCOMPLETE -2 |
||
2508 |
|||
2509 |
/* |
||
2510 |
* Callback-based SAX-like API. |
||
2511 |
* |
||
2512 |
* Property name and length is given only if it's available: i.e. if current |
||
2513 |
* event is an object's property. In other cases, `name` is `NULL`. For |
||
2514 |
* example, name is never given: |
||
2515 |
* - For the first value in the JSON string; |
||
2516 |
* - For events JSON_TYPE_OBJECT_END and JSON_TYPE_ARRAY_END |
||
2517 |
* |
||
2518 |
* E.g. for the input `{ "foo": 123, "bar": [ 1, 2, { "baz": true } ] }`, |
||
2519 |
* the sequence of callback invocations will be as follows: |
||
2520 |
* |
||
2521 |
* - type: JSON_TYPE_OBJECT_START, name: NULL, path: "", value: NULL |
||
2522 |
* - type: JSON_TYPE_NUMBER, name: "foo", path: ".foo", value: "123" |
||
2523 |
* - type: JSON_TYPE_ARRAY_START, name: "bar", path: ".bar", value: NULL |
||
2524 |
* - type: JSON_TYPE_NUMBER, name: "0", path: ".bar[0]", value: "1" |
||
2525 |
* - type: JSON_TYPE_NUMBER, name: "1", path: ".bar[1]", value: "2" |
||
2526 |
* - type: JSON_TYPE_OBJECT_START, name: "2", path: ".bar[2]", value: NULL |
||
2527 |
* - type: JSON_TYPE_TRUE, name: "baz", path: ".bar[2].baz", value: "true" |
||
2528 |
* - type: JSON_TYPE_OBJECT_END, name: NULL, path: ".bar[2]", value: "{ \"baz\": |
||
2529 |
*true }" |
||
2530 |
* - type: JSON_TYPE_ARRAY_END, name: NULL, path: ".bar", value: "[ 1, 2, { |
||
2531 |
*\"baz\": true } ]" |
||
2532 |
* - type: JSON_TYPE_OBJECT_END, name: NULL, path: "", value: "{ \"foo\": 123, |
||
2533 |
*\"bar\": [ 1, 2, { \"baz\": true } ] }" |
||
2534 |
*/ |
||
2535 |
typedef void (*json_walk_callback_t)(void *callback_data, const char *name, |
||
2536 |
size_t name_len, const char *path, |
||
2537 |
const struct json_token *token); |
||
2538 |
|||
2539 |
/* |
||
2540 |
* Parse `json_string`, invoking `callback` in a way similar to SAX parsers; |
||
2541 |
* see `json_walk_callback_t`. |
||
2542 |
* Return number of processed bytes, or a negative error code. |
||
2543 |
*/ |
||
2544 |
int json_walk(const char *json_string, int json_string_length, |
||
2545 |
json_walk_callback_t callback, void *callback_data); |
||
2546 |
|||
2547 |
/* |
||
2548 |
* JSON generation API. |
||
2549 |
* struct json_out abstracts output, allowing alternative printing plugins. |
||
2550 |
*/ |
||
2551 |
struct json_out { |
||
2552 |
int (*printer)(struct json_out *, const char *str, size_t len); |
||
2553 |
union { |
||
2554 |
struct { |
||
2555 |
char *buf; |
||
2556 |
size_t size; |
||
2557 |
size_t len; |
||
2558 |
} buf; |
||
2559 |
void *data; |
||
2560 |
FILE *fp; |
||
2561 |
} u; |
||
2562 |
}; |
||
2563 |
|||
2564 |
extern int json_printer_buf(struct json_out *, const char *, size_t); |
||
2565 |
extern int json_printer_file(struct json_out *, const char *, size_t); |
||
2566 |
|||
2567 |
#define JSON_OUT_BUF(buf, len) \ |
||
2568 |
{ \ |
||
2569 |
json_printer_buf, { \ |
||
2570 |
{ buf, len, 0 } \ |
||
2571 |
} \ |
||
2572 |
} |
||
2573 |
#define JSON_OUT_FILE(fp) \ |
||
2574 |
{ \ |
||
2575 |
json_printer_file, { \ |
||
2576 |
{ (char *) fp, 0, 0 } \ |
||
2577 |
} \ |
||
2578 |
} |
||
2579 |
|||
2580 |
typedef int (*json_printf_callback_t)(struct json_out *, va_list *ap); |
||
2581 |
|||
2582 |
/* |
||
2583 |
* Generate formatted output into a given sting buffer. |
||
2584 |
* This is a superset of printf() function, with extra format specifiers: |
||
2585 |
* - `%B` print json boolean, `true` or `false`. Accepts an `int`. |
||
2586 |
* - `%Q` print quoted escaped string or `null`. Accepts a `const char *`. |
||
2587 |
* - `%.*Q` same as `%Q`, but with length. Accepts `int`, `const char *` |
||
2588 |
* - `%V` print quoted base64-encoded string. Accepts a `const char *`, `int`. |
||
2589 |
* - `%H` print quoted hex-encoded string. Accepts a `int`, `const char *`. |
||
2590 |
* - `%M` invokes a json_printf_callback_t function. That callback function |
||
2591 |
* can consume more parameters. |
||
2592 |
* |
||
2593 |
* Return number of bytes printed. If the return value is bigger than the |
||
2594 |
* supplied buffer, that is an indicator of overflow. In the overflow case, |
||
2595 |
* overflown bytes are not printed. |
||
2596 |
*/ |
||
2597 |
int json_printf(struct json_out *, const char *fmt, ...); |
||
2598 |
int json_vprintf(struct json_out *, const char *fmt, va_list ap); |
||
2599 |
|||
2600 |
/* |
||
2601 |
* Same as json_printf, but prints to a file. |
||
2602 |
* File is created if does not exist. File is truncated if already exists. |
||
2603 |
*/ |
||
2604 |
int json_fprintf(const char *file_name, const char *fmt, ...); |
||
2605 |
int json_vfprintf(const char *file_name, const char *fmt, va_list ap); |
||
2606 |
|||
2607 |
/* |
||
2608 |
* Print JSON into an allocated 0-terminated string. |
||
2609 |
* Return allocated string, or NULL on error. |
||
2610 |
* Example: |
||
2611 |
* |
||
2612 |
* ```c |
||
2613 |
* char *str = json_asprintf("{a:%H}", 3, "abc"); |
||
2614 |
* printf("%s\n", str); // Prints "616263" |
||
2615 |
* free(str); |
||
2616 |
* ``` |
||
2617 |
*/ |
||
2618 |
char *json_asprintf(const char *fmt, ...); |
||
2619 |
char *json_vasprintf(const char *fmt, va_list ap); |
||
2620 |
|||
2621 |
/* |
||
2622 |
* Helper %M callback that prints contiguous C arrays. |
||
2623 |
* Consumes void *array_ptr, size_t array_size, size_t elem_size, char *fmt |
||
2624 |
* Return number of bytes printed. |
||
2625 |
*/ |
||
2626 |
int json_printf_array(struct json_out *, va_list *ap); |
||
2627 |
|||
2628 |
/* |
||
2629 |
* Scan JSON string `str`, performing scanf-like conversions according to `fmt`. |
||
2630 |
* This is a `scanf()` - like function, with following differences: |
||
2631 |
* |
||
2632 |
* 1. Object keys in the format string may be not quoted, e.g. "{key: %d}" |
||
2633 |
* 2. Order of keys in an object is irrelevant. |
||
2634 |
* 3. Several extra format specifiers are supported: |
||
2635 |
* - %B: consumes `int *` (or `char *`, if `sizeof(bool) == sizeof(char)`), |
||
2636 |
* expects boolean `true` or `false`. |
||
2637 |
* - %Q: consumes `char **`, expects quoted, JSON-encoded string. Scanned |
||
2638 |
* string is malloc-ed, caller must free() the string. |
||
2639 |
* - %V: consumes `char **`, `int *`. Expects base64-encoded string. |
||
2640 |
* Result string is base64-decoded, malloced and NUL-terminated. |
||
2641 |
* The length of result string is stored in `int *` placeholder. |
||
2642 |
* Caller must free() the result. |
||
2643 |
* - %H: consumes `int *`, `char **`. |
||
2644 |
* Expects a hex-encoded string, e.g. "fa014f". |
||
2645 |
* Result string is hex-decoded, malloced and NUL-terminated. |
||
2646 |
* The length of the result string is stored in `int *` placeholder. |
||
2647 |
* Caller must free() the result. |
||
2648 |
* - %M: consumes custom scanning function pointer and |
||
2649 |
* `void *user_data` parameter - see json_scanner_t definition. |
||
2650 |
* - %T: consumes `struct json_token *`, fills it out with matched token. |
||
2651 |
* |
||
2652 |
* Return number of elements successfully scanned & converted. |
||
2653 |
* Negative number means scan error. |
||
2654 |
*/ |
||
2655 |
int json_scanf(const char *str, int str_len, const char *fmt, ...); |
||
2656 |
int json_vscanf(const char *str, int str_len, const char *fmt, va_list ap); |
||
2657 |
|||
2658 |
/* json_scanf's %M handler */ |
||
2659 |
typedef void (*json_scanner_t)(const char *str, int len, void *user_data); |
||
2660 |
|||
2661 |
/* |
||
2662 |
* Helper function to scan array item with given path and index. |
||
2663 |
* Fills `token` with the matched JSON token. |
||
2664 |
* Return -1 if no array element found, otherwise non-negative token length. |
||
2665 |
*/ |
||
2666 |
int json_scanf_array_elem(const char *s, int len, const char *path, int index, |
||
2667 |
struct json_token *token); |
||
2668 |
|||
2669 |
/* |
||
2670 |
* Unescape JSON-encoded string src,slen into dst, dlen. |
||
2671 |
* src and dst may overlap. |
||
2672 |
* If destination buffer is too small (or zero-length), result string is not |
||
2673 |
* written but the length is counted nevertheless (similar to snprintf). |
||
2674 |
* Return the length of unescaped string in bytes. |
||
2675 |
*/ |
||
2676 |
int json_unescape(const char *src, int slen, char *dst, int dlen); |
||
2677 |
|||
2678 |
/* |
||
2679 |
* Escape a string `str`, `str_len` into the printer `out`. |
||
2680 |
* Return the number of bytes printed. |
||
2681 |
*/ |
||
2682 |
int json_escape(struct json_out *out, const char *str, size_t str_len); |
||
2683 |
|||
2684 |
/* |
||
2685 |
* Read the whole file in memory. |
||
2686 |
* Return malloc-ed file content, or NULL on error. The caller must free(). |
||
2687 |
*/ |
||
2688 |
char *json_fread(const char *file_name); |
||
2689 |
|||
2690 |
/* |
||
2691 |
* Update given JSON string `s,len` by changing the value at given `json_path`. |
||
2692 |
* The result is saved to `out`. If `json_fmt` == NULL, that deletes the key. |
||
2693 |
* If path is not present, missing keys are added. Array path without an |
||
2694 |
* index pushes a value to the end of an array. |
||
2695 |
* Return 1 if the string was changed, 0 otherwise. |
||
2696 |
* |
||
2697 |
* Example: s is a JSON string { "a": 1, "b": [ 2 ] } |
||
2698 |
* json_setf(s, len, out, ".a", "7"); // { "a": 7, "b": [ 2 ] } |
||
2699 |
* json_setf(s, len, out, ".b", "7"); // { "a": 1, "b": 7 } |
||
2700 |
* json_setf(s, len, out, ".b[]", "7"); // { "a": 1, "b": [ 2,7 ] } |
||
2701 |
* json_setf(s, len, out, ".b", NULL); // { "a": 1 } |
||
2702 |
*/ |
||
2703 |
int json_setf(const char *s, int len, struct json_out *out, |
||
2704 |
const char *json_path, const char *json_fmt, ...); |
||
2705 |
|||
2706 |
int json_vsetf(const char *s, int len, struct json_out *out, |
||
2707 |
const char *json_path, const char *json_fmt, va_list ap); |
||
2708 |
|||
2709 |
/* |
||
2710 |
* Pretty-print JSON string `s,len` into `out`. |
||
2711 |
* Return number of processed bytes in `s`. |
||
2712 |
*/ |
||
2713 |
int json_prettify(const char *s, int len, struct json_out *out); |
||
2714 |
|||
2715 |
/* |
||
2716 |
* Prettify JSON file `file_name`. |
||
2717 |
* Return number of processed bytes, or negative number of error. |
||
2718 |
* On error, file content is not modified. |
||
2719 |
*/ |
||
2720 |
int json_prettify_file(const char *file_name); |
||
2721 |
|||
2722 |
/* |
||
2723 |
* Iterate over an object at given JSON `path`. |
||
2724 |
* On each iteration, fill the `key` and `val` tokens. It is OK to pass NULL |
||
2725 |
* for `key`, or `val`, in which case they won't be populated. |
||
2726 |
* Return an opaque value suitable for the next iteration, or NULL when done. |
||
2727 |
* |
||
2728 |
* Example: |
||
2729 |
* |
||
2730 |
* ```c |
||
2731 |
* void *h = NULL; |
||
2732 |
* struct json_token key, val; |
||
2733 |
* while ((h = json_next_key(s, len, h, ".foo", &key, &val)) != NULL) { |
||
2734 |
* printf("[%.*s] -> [%.*s]\n", key.len, key.ptr, val.len, val.ptr); |
||
2735 |
* } |
||
2736 |
* ``` |
||
2737 |
*/ |
||
2738 |
void *json_next_key(const char *s, int len, void *handle, const char *path, |
||
2739 |
struct json_token *key, struct json_token *val); |
||
2740 |
|||
2741 |
/* |
||
2742 |
* Iterate over an array at given JSON `path`. |
||
2743 |
* Similar to `json_next_key`, but fills array index `idx` instead of `key`. |
||
2744 |
*/ |
||
2745 |
void *json_next_elem(const char *s, int len, void *handle, const char *path, |
||
2746 |
int *idx, struct json_token *val); |
||
2747 |
|||
2748 |
#ifdef __cplusplus |
||
2749 |
} |
||
2750 |
#endif /* __cplusplus */ |
||
2751 |
|||
2752 |
#endif /* CS_FROZEN_FROZEN_H_ */ |
||
2753 |
#ifdef MJS_MODULE_LINES |
||
2754 |
#line 1 "mjs/src/ffi/ffi.h" |
||
2755 |
#endif |
||
2756 |
/* |
||
2757 |
* Copyright (c) 2016 Cesanta Software Limited |
||
2758 |
* All rights reserved |
||
2759 |
*/ |
||
2760 |
|||
2761 |
#ifndef MJS_FFI_FFI_H_ |
||
2762 |
#define MJS_FFI_FFI_H_ |
||
2763 |
|||
2764 |
/* Amalgamated: #include "common/platform.h" */ |
||
2765 |
|||
2766 |
#if defined(__cplusplus) |
||
2767 |
extern "C" { |
||
2768 |
#endif /* __cplusplus */ |
||
2769 |
|||
2770 |
/* |
||
2771 |
* Maximum number of word-sized args to ffi-ed function. If at least one |
||
2772 |
* of the args is double, only 2 args are allowed. |
||
2773 |
*/ |
||
2774 |
#define FFI_MAX_ARGS_CNT 6 |
||
2775 |
|||
2776 |
typedef void(ffi_fn_t)(void); |
||
2777 |
|||
2778 |
typedef intptr_t ffi_word_t; |
||
2779 |
|||
2780 |
enum ffi_ctype { |
||
2781 |
FFI_CTYPE_WORD, |
||
2782 |
FFI_CTYPE_BOOL, |
||
2783 |
FFI_CTYPE_FLOAT, |
||
2784 |
FFI_CTYPE_DOUBLE, |
||
2785 |
}; |
||
2786 |
|||
2787 |
struct ffi_arg { |
||
2788 |
enum ffi_ctype ctype; |
||
2789 |
union { |
||
2790 |
uint64_t i; |
||
2791 |
double d; |
||
2792 |
float f; |
||
2793 |
} v; |
||
2794 |
}; |
||
2795 |
|||
2796 |
int ffi_call(ffi_fn_t *func, int nargs, struct ffi_arg *res, |
||
2797 |
struct ffi_arg *args); |
||
2798 |
|||
2799 |
void ffi_set_word(struct ffi_arg *arg, ffi_word_t v); |
||
2800 |
void ffi_set_bool(struct ffi_arg *arg, bool v); |
||
2801 |
void ffi_set_ptr(struct ffi_arg *arg, void *v); |
||
2802 |
void ffi_set_double(struct ffi_arg *arg, double v); |
||
2803 |
void ffi_set_float(struct ffi_arg *arg, float v); |
||
2804 |
|||
2805 |
#if defined(__cplusplus) |
||
2806 |
} |
||
2807 |
#endif /* __cplusplus */ |
||
2808 |
|||
2809 |
#endif /* MJS_FFI_FFI_H_ */ |
||
2810 |
#ifdef MJS_MODULE_LINES |
||
2811 |
#line 1 "mjs/src/mjs_internal.h" |
||
2812 |
#endif |
||
2813 |
/* |
||
2814 |
* Copyright (c) 2016 Cesanta Software Limited |
||
2815 |
* All rights reserved |
||
2816 |
*/ |
||
2817 |
|||
2818 |
#ifndef MJS_INTERNAL_H_ |
||
2819 |
#define MJS_INTERNAL_H_ |
||
2820 |
|||
2821 |
#include <assert.h> |
||
2822 |
#include <ctype.h> |
||
2823 |
#include <math.h> |
||
2824 |
#include <stdarg.h> |
||
2825 |
#include <stdio.h> |
||
2826 |
#include <string.h> |
||
2827 |
|||
2828 |
#ifndef FAST |
||
2829 |
#define FAST |
||
2830 |
#endif |
||
2831 |
|||
2832 |
#ifndef STATIC |
||
2833 |
#define STATIC |
||
2834 |
#endif |
||
2835 |
|||
2836 |
#ifndef ENDL |
||
2837 |
#define ENDL "\n" |
||
2838 |
#endif |
||
2839 |
|||
2840 |
#ifdef MJS_EXPOSE_PRIVATE |
||
2841 |
#define MJS_PRIVATE |
||
2842 |
#define MJS_EXTERN extern |
||
2843 |
#else |
||
2844 |
#define MJS_PRIVATE static |
||
2845 |
#define MJS_EXTERN static |
||
2846 |
#endif |
||
2847 |
|||
2848 |
#ifndef ARRAY_SIZE |
||
2849 |
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) |
||
2850 |
#endif |
||
2851 |
|||
2852 |
#if !defined(WEAK) |
||
2853 |
#if (defined(__GNUC__) || defined(__TI_COMPILER_VERSION__)) && !defined(_WIN32) |
||
2854 |
#define WEAK __attribute__((weak)) |
||
2855 |
#else |
||
2856 |
#define WEAK |
||
2857 |
#endif |
||
2858 |
#endif |
||
2859 |
|||
2860 |
#ifndef CS_ENABLE_STDIO |
||
2861 |
#define CS_ENABLE_STDIO 1 |
||
2862 |
#endif |
||
2863 |
|||
2864 |
/* Amalgamated: #include "common/cs_dbg.h" */ |
||
2865 |
/* Amalgamated: #include "common/cs_file.h" */ |
||
2866 |
/* Amalgamated: #include "common/mbuf.h" */ |
||
2867 |
|||
2868 |
#if defined(_WIN32) && _MSC_VER < 1700 |
||
2869 |
typedef signed char int8_t; |
||
2870 |
typedef unsigned char uint8_t; |
||
2871 |
typedef int int32_t; |
||
2872 |
typedef unsigned int uint32_t; |
||
2873 |
typedef short int16_t; |
||
2874 |
typedef unsigned short uint16_t; |
||
2875 |
typedef __int64 int64_t; |
||
2876 |
typedef unsigned long uintptr_t; |
||
2877 |
#define STRX(x) #x |
||
2878 |
#define STR(x) STRX(x) |
||
2879 |
#define __func__ __FILE__ ":" STR(__LINE__) |
||
2880 |
// #define snprintf _snprintf |
||
2881 |
#define vsnprintf _vsnprintf |
||
2882 |
#define isnan(x) _isnan(x) |
||
2883 |
#define va_copy(x, y) (x) = (y) |
||
2884 |
#define CS_DEFINE_DIRENT |
||
2885 |
#include <windows.h> |
||
2886 |
#else |
||
2887 |
#if defined(__unix__) || defined(__APPLE__) |
||
2888 |
#include <dlfcn.h> |
||
2889 |
#endif |
||
2890 |
#endif |
||
2891 |
|||
2892 |
/* |
||
2893 |
* Number of bytes reserved for the jump offset initially. The most practical |
||
2894 |
* value is 1, but for testing it's useful to set it to 0 and to some large |
||
2895 |
* value as well (like, 4), to make sure that the code behaves correctly under |
||
2896 |
* all circumstances. |
||
2897 |
*/ |
||
2898 |
#ifndef MJS_INIT_OFFSET_SIZE |
||
2899 |
#define MJS_INIT_OFFSET_SIZE 1 |
||
2900 |
#endif |
||
2901 |
|||
2902 |
#endif /* MJS_INTERNAL_H_ */ |
||
2903 |
#ifdef MJS_MODULE_LINES |
||
2904 |
#line 1 "mjs/src/mjs_license.h" |
||
2905 |
#endif |
||
2906 |
/* |
||
2907 |
* Copyright (c) 2017 Cesanta Software Limited |
||
2908 |
* All rights reserved |
||
2909 |
* |
||
2910 |
* This software is dual-licensed: you can redistribute it and/or modify |
||
2911 |
* it under the terms of the GNU General Public License version 2 as |
||
2912 |
* published by the Free Software Foundation. For the terms of this |
||
2913 |
* license, see <http://www.gnu.org/licenses/>. |
||
2914 |
* |
||
2915 |
* You are free to use this software under the terms of the GNU General |
||
2916 |
* Public License, but WITHOUT ANY WARRANTY; without even the implied |
||
2917 |
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
||
2918 |
* See the GNU General Public License for more details. |
||
2919 |
* |
||
2920 |
* Alternatively, you can license this software under a commercial |
||
2921 |
* license, as set out in <https://www.cesanta.com/license>. |
||
2922 |
*/ |
||
2923 |
#ifdef MJS_MODULE_LINES |
||
2924 |
#line 1 "mjs/src/mjs_features.h" |
||
2925 |
#endif |
||
2926 |
/* |
||
2927 |
* Copyright (c) 2017 Cesanta Software Limited |
||
2928 |
* All rights reserved |
||
2929 |
*/ |
||
2930 |
|||
2931 |
#ifndef MJS_FEATURES_H_ |
||
2932 |
#define MJS_FEATURES_H_ |
||
2933 |
|||
2934 |
#if !defined(MJS_AGGRESSIVE_GC) |
||
2935 |
#define MJS_AGGRESSIVE_GC 0 |
||
2936 |
#endif |
||
2937 |
|||
2938 |
#if !defined(MJS_MEMORY_STATS) |
||
2939 |
#define MJS_MEMORY_STATS 0 |
||
2940 |
#endif |
||
2941 |
|||
2942 |
/* |
||
2943 |
* MJS_GENERATE_JSC: if enabled, and if mmapping is also enabled (CS_MMAP), |
||
2944 |
* then execution of any .js file will result in creation of a .jsc file with |
||
2945 |
* precompiled bcode, and this .jsc file will be mmapped, instead of keeping |
||
2946 |
* bcode in RAM. |
||
2947 |
* |
||
2948 |
* By default it's enabled (provided that CS_MMAP is defined) |
||
2949 |
*/ |
||
2950 |
#if !defined(MJS_GENERATE_JSC) |
||
2951 |
#if defined(CS_MMAP) |
||
2952 |
#define MJS_GENERATE_JSC 1 |
||
2953 |
#else |
||
2954 |
#define MJS_GENERATE_JSC 0 |
||
2955 |
#endif |
||
2956 |
#endif |
||
2957 |
|||
2958 |
#endif /* MJS_FEATURES_H_ */ |
||
2959 |
#ifdef MJS_MODULE_LINES |
||
2960 |
#line 1 "mjs/src/mjs_core_public.h" |
||
2961 |
#endif |
||
2962 |
/* |
||
2963 |
* Copyright (c) 2016 Cesanta Software Limited |
||
2964 |
* All rights reserved |
||
2965 |
*/ |
||
2966 |
|||
2967 |
#ifndef MJS_CORE_PUBLIC_H_ |
||
2968 |
#define MJS_CORE_PUBLIC_H_ |
||
2969 |
|||
2970 |
#if !defined(_MSC_VER) || _MSC_VER >= 1700 |
||
2971 |
#include <stdint.h> |
||
2972 |
#else |
||
2973 |
typedef unsigned __int64 uint64_t; |
||
2974 |
typedef int int32_t; |
||
2975 |
typedef unsigned char uint8_t; |
||
2976 |
#endif |
||
2977 |
#include <stdio.h> |
||
2978 |
#include <stddef.h> |
||
2979 |
/* Amalgamated: #include "mjs/src/mjs_license.h" */ |
||
2980 |
/* Amalgamated: #include "mjs/src/mjs_features.h" */ |
||
2981 |
|||
2982 |
#if defined(__cplusplus) |
||
2983 |
extern "C" { |
||
2984 |
#endif /* __cplusplus */ |
||
2985 |
|||
2986 |
#define MJS_ENABLE_DEBUG 1 |
||
2987 |
|||
2988 |
/* |
||
2989 |
* Double-precision floating-point number, IEEE 754 |
||
2990 |
* |
||
2991 |
* 64 bit (8 bytes) in total |
||
2992 |
* 1 bit sign |
||
2993 |
* 11 bits exponent |
||
2994 |
* 52 bits mantissa |
||
2995 |
* 7 6 5 4 3 2 1 0 |
||
2996 |
* seeeeeee|eeeemmmm|mmmmmmmm|mmmmmmmm|mmmmmmmm|mmmmmmmm|mmmmmmmm|mmmmmmmm |
||
2997 |
* |
||
2998 |
* If an exponent is all-1 and mantissa is all-0, then it is an INFINITY: |
||
2999 |
* 11111111|11110000|00000000|00000000|00000000|00000000|00000000|00000000 |
||
3000 |
* |
||
3001 |
* If an exponent is all-1 and mantissa's MSB is 1, it is a quiet NaN: |
||
3002 |
* 11111111|11111000|00000000|00000000|00000000|00000000|00000000|00000000 |
||
3003 |
* |
||
3004 |
* MJS NaN-packing: |
||
3005 |
* sign and exponent is 0xfff |
||
3006 |
* 4 bits specify type (tag), must be non-zero |
||
3007 |
* 48 bits specify value |
||
3008 |
* |
||
3009 |
* 11111111|1111tttt|vvvvvvvv|vvvvvvvv|vvvvvvvv|vvvvvvvv|vvvvvvvv|vvvvvvvv |
||
3010 |
* NaN marker |type| 48-bit placeholder for values: pointers, strings |
||
3011 |
* |
||
3012 |
* On 64-bit platforms, pointers are really 48 bit only, so they can fit, |
||
3013 |
* provided they are sign extended |
||
3014 |
*/ |
||
3015 |
|||
3016 |
typedef uint64_t mjs_val_t; |
||
3017 |
|||
3018 |
/* This if-0 is a dirty workaround to force etags to pick `struct mjs` */ |
||
3019 |
#if 0 |
||
3020 |
/* Opaque structure. MJS engine context. */ |
||
3021 |
struct mjs { |
||
3022 |
/* ... */ |
||
3023 |
}; |
||
3024 |
#endif |
||
3025 |
|||
3026 |
struct mjs; |
||
3027 |
|||
3028 |
typedef enum mjs_err { |
||
3029 |
MJS_OK, |
||
3030 |
MJS_SYNTAX_ERROR, |
||
3031 |
MJS_REFERENCE_ERROR, |
||
3032 |
MJS_TYPE_ERROR, |
||
3033 |
MJS_OUT_OF_MEMORY, |
||
3034 |
MJS_INTERNAL_ERROR, |
||
3035 |
MJS_NOT_IMPLEMENTED_ERROR, |
||
3036 |
MJS_FILE_READ_ERROR, |
||
3037 |
MJS_BAD_ARGS_ERROR, |
||
3038 |
|||
3039 |
MJS_ERRS_CNT |
||
3040 |
} mjs_err_t; |
||
3041 |
struct mjs; |
||
3042 |
|||
3043 |
/* Create MJS instance */ |
||
3044 |
struct mjs *mjs_create(); |
||
3045 |
|||
3046 |
struct mjs_create_opts { |
||
3047 |
/* use non-default bytecode definition file, testing-only */ |
||
3048 |
const struct bf_code *code; |
||
3049 |
}; |
||
3050 |
|||
3051 |
/* |
||
3052 |
* Like `msj_create()`, but allows to customize initial MJS state, see `struct |
||
3053 |
* mjs_create_opts`. |
||
3054 |
*/ |
||
3055 |
struct mjs *mjs_create_opt(struct mjs_create_opts opts); |
||
3056 |
|||
3057 |
/* Destroy MJS instance */ |
||
3058 |
void mjs_destroy(struct mjs *mjs); |
||
3059 |
|||
3060 |
mjs_val_t mjs_get_global(struct mjs *mjs); |
||
3061 |
|||
3062 |
/* |
||
3063 |
* Tells the GC about an MJS value variable/field owned by C code. |
||
3064 |
* |
||
3065 |
* The user's C code should own mjs_val_t variables if the value's lifetime |
||
3066 |
* crosses any invocation of `mjs_exec()` and friends, including `mjs_call()`. |
||
3067 |
* |
||
3068 |
* The registration of the variable prevents the GC from mistakenly treat the |
||
3069 |
* object as garbage. |
||
3070 |
* |
||
3071 |
* User code should also explicitly disown the variables with `mjs_disown()` |
||
3072 |
* once it goes out of scope or the structure containing the mjs_val_t field is |
||
3073 |
* freed. |
||
3074 |
* |
||
3075 |
* Consider the following examples: |
||
3076 |
* |
||
3077 |
* Correct (owning is not necessary): |
||
3078 |
* ```c |
||
3079 |
* mjs_val_t res; |
||
3080 |
* mjs_exec(mjs, "....some script", &res); |
||
3081 |
* // ... use res somehow |
||
3082 |
* |
||
3083 |
* mjs_val_t res; |
||
3084 |
* mjs_exec(mjs, "....some script2", &res); |
||
3085 |
* // ... use new res somehow |
||
3086 |
* ``` |
||
3087 |
* |
||
3088 |
* WRONG: |
||
3089 |
* ```c |
||
3090 |
* mjs_val_t res1; |
||
3091 |
* mjs_exec(mjs, "....some script", &res1); |
||
3092 |
* |
||
3093 |
* mjs_val_t res2; |
||
3094 |
* mjs_exec(mjs, "....some script2", &res2); |
||
3095 |
* |
||
3096 |
* // ... use res1 (WRONG!) and res2 |
||
3097 |
* ``` |
||
3098 |
* |
||
3099 |
* The code above is wrong, because after the second invocation of |
||
3100 |
* `mjs_exec()`, the value of `res1` is invalidated. |
||
3101 |
* |
||
3102 |
* Correct (res1 is owned) |
||
3103 |
* ```c |
||
3104 |
* mjs_val_t res1 = MJS_UNDEFINED; |
||
3105 |
* mjs_own(mjs, &res1); |
||
3106 |
* mjs_exec(mjs, "....some script", &res1); |
||
3107 |
* |
||
3108 |
* mjs_val_t res2 = MJS_UNDEFINED; |
||
3109 |
* mjs_exec(mjs, "....some script2", &res2); |
||
3110 |
* |
||
3111 |
* // ... use res1 and res2 |
||
3112 |
* mjs_disown(mjs, &res1); |
||
3113 |
* ``` |
||
3114 |
* |
||
3115 |
* NOTE that we explicly initialized `res1` to a valid value before owning it |
||
3116 |
* (in this case, the value is `MJS_UNDEFINED`). Owning an uninitialized |
||
3117 |
* variable is an undefined behaviour. |
||
3118 |
* |
||
3119 |
* Of course, it's not an error to own a variable even if it's not mandatory: |
||
3120 |
* e.g. in the last example we could own both `res1` and `res2`. Probably it |
||
3121 |
* would help us in the future, when we refactor the code so that `res2` has to |
||
3122 |
* be owned, and we could forget to do that. |
||
3123 |
* |
||
3124 |
* Also, if the user code has some C function called from MJS, and in this C |
||
3125 |
* function some MJS value (`mjs_val_t`) needs to be stored somewhhere and to |
||
3126 |
* stay alive after the C function has returned, it also needs to be properly |
||
3127 |
* owned. |
||
3128 |
*/ |
||
3129 |
void mjs_own(struct mjs *mjs, mjs_val_t *v); |
||
3130 |
|||
3131 |
/* |
||
3132 |
* Disowns the value previously owned by `mjs_own()`. |
||
3133 |
* |
||
3134 |
* Returns 1 if value is found, 0 otherwise. |
||
3135 |
*/ |
||
3136 |
int mjs_disown(struct mjs *mjs, mjs_val_t *v); |
||
3137 |
|||
3138 |
mjs_err_t mjs_set_errorf(struct mjs *mjs, mjs_err_t err, const char *fmt, ...); |
||
3139 |
|||
3140 |
/* |
||
3141 |
* If there is no error message already set, then it's equal to |
||
3142 |
* `mjs_set_errorf()`. |
||
3143 |
* |
||
3144 |
* Otherwise, an old message gets prepended with the new one, followed by a |
||
3145 |
* colon. (the previously set error code is kept) |
||
3146 |
*/ |
||
3147 |
mjs_err_t mjs_prepend_errorf(struct mjs *mjs, mjs_err_t err, const char *fmt, |
||
3148 |
...); |
||
3149 |
|||
3150 |
/* |
||
3151 |
* Print the last error details. If print_stack_trace is non-zero, also |
||
3152 |
* print stack trace. `msg` is the message which gets prepended to the actual |
||
3153 |
* error message, if it's NULL, then "MJS error" is used. |
||
3154 |
*/ |
||
3155 |
void mjs_print_error(struct mjs *mjs, FILE *fp, const char *msg, |
||
3156 |
int print_stack_trace); |
||
3157 |
|||
3158 |
/* |
||
3159 |
* return a string representation of an error. |
||
3160 |
* the error string might be overwritten by calls to `mjs_set_errorf`. |
||
3161 |
*/ |
||
3162 |
const char *mjs_strerror(struct mjs *mjs, enum mjs_err err); |
||
3163 |
|||
3164 |
/* |
||
3165 |
* Sets whether *.jsc files are generated when *.js file is executed. By |
||
3166 |
* default it's 0. |
||
3167 |
* |
||
3168 |
* If either `MJS_GENERATE_JSC` or `CS_MMAP` is off, then this function has no |
||
3169 |
* effect. |
||
3170 |
*/ |
||
3171 |
void mjs_set_generate_jsc(struct mjs *mjs, int generate_jsc); |
||
3172 |
|||
3173 |
/* |
||
3174 |
* When invoked from a cfunction, returns number of arguments passed to the |
||
3175 |
* current JS function call. |
||
3176 |
*/ |
||
3177 |
int mjs_nargs(struct mjs *mjs); |
||
3178 |
|||
3179 |
/* |
||
3180 |
* When invoked from a cfunction, returns n-th argument to the current JS |
||
3181 |
* function call. |
||
3182 |
*/ |
||
3183 |
mjs_val_t mjs_arg(struct mjs *mjs, int n); |
||
3184 |
|||
3185 |
/* |
||
3186 |
* Sets return value for the current JS function call. |
||
3187 |
*/ |
||
3188 |
void mjs_return(struct mjs *mjs, mjs_val_t v); |
||
3189 |
|||
3190 |
#if defined(__cplusplus) |
||
3191 |
} |
||
3192 |
#endif /* __cplusplus */ |
||
3193 |
|||
3194 |
#endif /* MJS_CORE_PUBLIC_H_ */ |
||
3195 |
#ifdef MJS_MODULE_LINES |
||
3196 |
#line 1 "mjs/src/mjs_array_public.h" |
||
3197 |
#endif |
||
3198 |
/* |
||
3199 |
* Copyright (c) 2017 Cesanta Software Limited |
||
3200 |
* All rights reserved |
||
3201 |
*/ |
||
3202 |
|||
3203 |
/* |
||
3204 |
* === Arrays |
||
3205 |
*/ |
||
3206 |
|||
3207 |
#ifndef MJS_ARRAY_PUBLIC_H_ |
||
3208 |
#define MJS_ARRAY_PUBLIC_H_ |
||
3209 |
|||
3210 |
/* Amalgamated: #include "mjs/src/mjs_core_public.h" */ |
||
3211 |
|||
3212 |
#if defined(__cplusplus) |
||
3213 |
extern "C" { |
||
3214 |
#endif /* __cplusplus */ |
||
3215 |
|||
3216 |
/* Make an empty array object */ |
||
3217 |
mjs_val_t mjs_mk_array(struct mjs *mjs); |
||
3218 |
|||
3219 |
/* Returns length on an array. If `arr` is not an array, 0 is returned. */ |
||
3220 |
unsigned long mjs_array_length(struct mjs *mjs, mjs_val_t arr); |
||
3221 |
|||
3222 |
/* Insert value `v` in array `arr` at the end of the array. */ |
||
3223 |
mjs_err_t mjs_array_push(struct mjs *mjs, mjs_val_t arr, mjs_val_t v); |
||
3224 |
|||
3225 |
/* |
||
3226 |
* Return array member at index `index`. If `index` is out of bounds, undefined |
||
3227 |
* is returned. |
||
3228 |
*/ |
||
3229 |
mjs_val_t mjs_array_get(struct mjs *, mjs_val_t arr, unsigned long index); |
||
3230 |
|||
3231 |
/* Insert value `v` into `arr` at index `index`. */ |
||
3232 |
mjs_err_t mjs_array_set(struct mjs *mjs, mjs_val_t arr, unsigned long index, |
||
3233 |
mjs_val_t v); |
||
3234 |
|||
3235 |
/* Returns true if the given value is an array */ |
||
3236 |
int mjs_is_array(mjs_val_t v); |
||
3237 |
|||
3238 |
/* Delete value in array `arr` at index `index`, if it exists. */ |
||
3239 |
void mjs_array_del(struct mjs *mjs, mjs_val_t arr, unsigned long index); |
||
3240 |
|||
3241 |
#if defined(__cplusplus) |
||
3242 |
} |
||
3243 |
#endif /* __cplusplus */ |
||
3244 |
|||
3245 |
#endif /* MJS_ARRAY_PUBLIC_H_ */ |
||
3246 |
#ifdef MJS_MODULE_LINES |
||
3247 |
#line 1 "mjs/src/mjs_array.h" |
||
3248 |
#endif |
||
3249 |
/* |
||
3250 |
* Copyright (c) 2014 Cesanta Software Limited |
||
3251 |
* All rights reserved |
||
3252 |
*/ |
||
3253 |
|||
3254 |
#ifndef MJS_ARRAY_H_ |
||
3255 |
#define MJS_ARRAY_H_ |
||
3256 |
|||
3257 |
/* Amalgamated: #include "mjs/src/mjs_internal.h" */ |
||
3258 |
/* Amalgamated: #include "mjs/src/mjs_array_public.h" */ |
||
3259 |
|||
3260 |
#if defined(__cplusplus) |
||
3261 |
extern "C" { |
||
3262 |
#endif /* __cplusplus */ |
||
3263 |
|||
3264 |
MJS_PRIVATE mjs_val_t |
||
3265 |
mjs_array_get2(struct mjs *mjs, mjs_val_t arr, unsigned long index, int *has); |
||
3266 |
|||
3267 |
MJS_PRIVATE void mjs_array_splice(struct mjs *mjs); |
||
3268 |
|||
3269 |
MJS_PRIVATE void mjs_array_push_internal(struct mjs *mjs); |
||
3270 |
|||
3271 |
#if defined(__cplusplus) |
||
3272 |
} |
||
3273 |
#endif /* __cplusplus */ |
||
3274 |
|||
3275 |
#endif /* MJS_ARRAY_H_ */ |
||
3276 |
#ifdef MJS_MODULE_LINES |
||
3277 |
#line 1 "mjs/src/mjs_ffi_public.h" |
||
3278 |
#endif |
||
3279 |
/* |
||
3280 |
* Copyright (c) 2016 Cesanta Software Limited |
||
3281 |
* All rights reserved |
||
3282 |
*/ |
||
3283 |
|||
3284 |
#ifndef MJS_FFI_PUBLIC_H_ |
||
3285 |
#define MJS_FFI_PUBLIC_H_ |
||
3286 |
|||
3287 |
/* Amalgamated: #include "mjs/src/mjs_core_public.h" */ |
||
3288 |
|||
3289 |
#if defined(__cplusplus) |
||
3290 |
extern "C" { |
||
3291 |
#endif /* __cplusplus */ |
||
3292 |
|||
3293 |
enum mjs_ffi_ctype { |
||
3294 |
MJS_FFI_CTYPE_NONE, |
||
3295 |
MJS_FFI_CTYPE_USERDATA, |
||
3296 |
MJS_FFI_CTYPE_CALLBACK, |
||
3297 |
MJS_FFI_CTYPE_INT, |
||
3298 |
MJS_FFI_CTYPE_BOOL, |
||
3299 |
MJS_FFI_CTYPE_DOUBLE, |
||
3300 |
MJS_FFI_CTYPE_FLOAT, |
||
3301 |
MJS_FFI_CTYPE_CHAR_PTR, |
||
3302 |
MJS_FFI_CTYPE_VOID_PTR, |
||
3303 |
MJS_FFI_CTYPE_STRUCT_MG_STR_PTR, |
||
3304 |
MJS_FFI_CTYPE_STRUCT_MG_STR, |
||
3305 |
MJS_FFI_CTYPE_INVALID, |
||
3306 |
}; |
||
3307 |
|||
3308 |
typedef void *(mjs_ffi_resolver_t)(void *handle, const char *symbol); |
||
3309 |
|||
3310 |
void mjs_set_ffi_resolver(struct mjs *mjs, mjs_ffi_resolver_t *dlsym); |
||
3311 |
|||
3312 |
#if defined(__cplusplus) |
||
3313 |
} |
||
3314 |
#endif /* __cplusplus */ |
||
3315 |
|||
3316 |
#endif /* MJS_FFI_PUBLIC_H_ */ |
||
3317 |
#ifdef MJS_MODULE_LINES |
||
3318 |
#line 1 "mjs/src/mjs_ffi.h" |
||
3319 |
#endif |
||
3320 |
/* |
||
3321 |
* Copyright (c) 2017 Cesanta Software Limited |
||
3322 |
* All rights reserved |
||
3323 |
*/ |
||
3324 |
|||
3325 |
#ifndef MJS_FFI_H_ |
||
3326 |
#define MJS_FFI_H_ |
||
3327 |
|||
3328 |
/* Amalgamated: #include "mjs/src/ffi/ffi.h" */ |
||
3329 |
/* Amalgamated: #include "mjs/src/mjs_ffi_public.h" */ |
||
3330 |
/* Amalgamated: #include "mjs/src/mjs_internal.h" */ |
||
3331 |
|||
3332 |
#if defined(__cplusplus) |
||
3333 |
extern "C" { |
||
3334 |
#endif /* __cplusplus */ |
||
3335 |
|||
3336 |
mjs_ffi_resolver_t dlsym; |
||
3337 |
|||
3338 |
#define MJS_CB_ARGS_MAX_CNT 6 |
||
3339 |
#define MJS_CB_SIGNATURE_MAX_SIZE (MJS_CB_ARGS_MAX_CNT + 1 /* return type */) |
||
3340 |
|||
3341 |
typedef uint8_t mjs_ffi_ctype_t; |
||
3342 |
|||
3343 |
enum ffi_sig_type { |
||
3344 |
FFI_SIG_FUNC, |
||
3345 |
FFI_SIG_CALLBACK, |
||
3346 |
}; |
||
3347 |
|||
3348 |
/* |
||
3349 |
* Parsed FFI signature |
||
3350 |
*/ |
||
3351 |
struct mjs_ffi_sig { |
||
3352 |
/* |
||
3353 |
* Callback signature, corresponds to the arg of type MJS_FFI_CTYPE_CALLBACK |
||
3354 |
* TODO(dfrank): probably we'll need to support multiple callback/userdata |
||
3355 |
* pairs |
||
3356 |
* |
||
3357 |
* NOTE(dfrank): instances of this structure are grouped into GC arenas and |
||
3358 |
* managed by GC, and for the GC mark to work, the first element should be |
||
3359 |
* a pointer (so that the two LSBs are not used). |
||
3360 |
*/ |
||
3361 |
struct mjs_ffi_sig *cb_sig; |
||
3362 |
|||
3363 |
/* |
||
3364 |
* The first item is the return value type (for `void`, `MJS_FFI_CTYPE_NONE` |
||
3365 |
* is used); the rest are arguments. If some argument is |
||
3366 |
* `MJS_FFI_CTYPE_NONE`, it means that there are no more arguments. |
||
3367 |
*/ |
||
3368 |
mjs_ffi_ctype_t val_types[MJS_CB_SIGNATURE_MAX_SIZE]; |
||
3369 |
|||
3370 |
/* |
||
3371 |
* Function to call. If `is_callback` is not set, then it's the function |
||
3372 |
* obtained by dlsym; otherwise it's a pointer to the appropriate callback |
||
3373 |
* implementation. |
||
3374 |
*/ |
||
3375 |
ffi_fn_t *fn; |
||
3376 |
|||
3377 |
/* Number of arguments in the signature */ |
||
3378 |
int8_t args_cnt; |
||
3379 |
|||
3380 |
/* |
||
3381 |
* If set, then the signature represents the callback (as opposed to a normal |
||
3382 |
* function), and `fn` points to the suitable callback implementation. |
||
3383 |
*/ |
||
3384 |
unsigned is_callback : 1; |
||
3385 |
unsigned is_valid : 1; |
||
3386 |
}; |
||
3387 |
typedef struct mjs_ffi_sig mjs_ffi_sig_t; |
||
3388 |
|||
3389 |
/* Initialize new FFI signature */ |
||
3390 |
MJS_PRIVATE void mjs_ffi_sig_init(mjs_ffi_sig_t *sig); |
||
3391 |
/* Copy existing FFI signature */ |
||
3392 |
MJS_PRIVATE void mjs_ffi_sig_copy(mjs_ffi_sig_t *to, const mjs_ffi_sig_t *from); |
||
3393 |
/* Free FFI signature. NOTE: the pointer `sig` itself is not freed */ |
||
3394 |
MJS_PRIVATE void mjs_ffi_sig_free(mjs_ffi_sig_t *sig); |
||
3395 |
|||
3396 |
/* |
||
3397 |
* Creates a new FFI signature from the GC arena, and return mjs_val_t which |
||
3398 |
* wraps it. |
||
3399 |
*/ |
||
3400 |
MJS_PRIVATE mjs_val_t mjs_mk_ffi_sig(struct mjs *mjs); |
||
3401 |
|||
3402 |
/* |
||
3403 |
* Checks whether the given value is a FFI signature. |
||
3404 |
*/ |
||
3405 |
MJS_PRIVATE int mjs_is_ffi_sig(mjs_val_t v); |
||
3406 |
|||
3407 |
/* |
||
3408 |
* Wraps FFI signature structure into mjs_val_t value. |
||
3409 |
*/ |
||
3410 |
MJS_PRIVATE mjs_val_t mjs_ffi_sig_to_value(struct mjs_ffi_sig *psig); |
||
3411 |
|||
3412 |
/* |
||
3413 |
* Extracts a pointer to the FFI signature struct from the mjs_val_t value. |
||
3414 |
*/ |
||
3415 |
MJS_PRIVATE struct mjs_ffi_sig *mjs_get_ffi_sig_struct(mjs_val_t v); |
||
3416 |
|||
3417 |
/* |
||
3418 |
* A wrapper for mjs_ffi_sig_free() suitable to use as a GC cell destructor. |
||
3419 |
*/ |
||
3420 |
MJS_PRIVATE void mjs_ffi_sig_destructor(struct mjs *mjs, void *psig); |
||
3421 |
|||
3422 |
MJS_PRIVATE int mjs_ffi_sig_set_val_type(mjs_ffi_sig_t *sig, int idx, |
||
3423 |
mjs_ffi_ctype_t type); |
||
3424 |
MJS_PRIVATE int mjs_ffi_sig_validate(struct mjs *mjs, mjs_ffi_sig_t *sig, |
||
3425 |
enum ffi_sig_type sig_type); |
||
3426 |
MJS_PRIVATE int mjs_ffi_is_regular_word(mjs_ffi_ctype_t type); |
||
3427 |
MJS_PRIVATE int mjs_ffi_is_regular_word_or_void(mjs_ffi_ctype_t type); |
||
3428 |
|||
3429 |
struct mjs_ffi_cb_args { |
||
3430 |
struct mjs_ffi_cb_args *next; |
||
3431 |
struct mjs *mjs; |
||
3432 |
mjs_ffi_sig_t sig; |
||
3433 |
mjs_val_t func; |
||
3434 |
mjs_val_t userdata; |
||
3435 |
}; |
||
3436 |
typedef struct mjs_ffi_cb_args ffi_cb_args_t; |
||
3437 |
|||
3438 |
/* |
||
3439 |
* cfunction: |
||
3440 |
* Parses the FFI signature string and returns a value wrapping mjs_ffi_sig_t. |
||
3441 |
*/ |
||
3442 |
MJS_PRIVATE mjs_err_t mjs_ffi_call(struct mjs *mjs); |
||
3443 |
|||
3444 |
/* |
||
3445 |
* cfunction: |
||
3446 |
* Performs the FFI signature call. |
||
3447 |
*/ |
||
3448 |
MJS_PRIVATE mjs_err_t mjs_ffi_call2(struct mjs *mjs); |
||
3449 |
|||
3450 |
MJS_PRIVATE void mjs_ffi_cb_free(struct mjs *); |
||
3451 |
MJS_PRIVATE void mjs_ffi_args_free_list(struct mjs *mjs); |
||
3452 |
|||
3453 |
#if defined(__cplusplus) |
||
3454 |
} |
||
3455 |
#endif /* __cplusplus */ |
||
3456 |
|||
3457 |
#endif /* MJS_FFI_H_ */ |
||
3458 |
#ifdef MJS_MODULE_LINES |
||
3459 |
#line 1 "mjs/src/mjs_mm.h" |
||
3460 |
#endif |
||
3461 |
/* |
||
3462 |
* Copyright (c) 2014-2016 Cesanta Software Limited |
||
3463 |
* All rights reserved |
||
3464 |
*/ |
||
3465 |
|||
3466 |
#ifndef MJS_MM_H_ |
||
3467 |
#define MJS_MM_H_ |
||
3468 |
|||
3469 |
/* Amalgamated: #include "mjs/src/mjs_internal.h" */ |
||
3470 |
|||
3471 |
#if defined(__cplusplus) |
||
3472 |
extern "C" { |
||
3473 |
#endif /* __cplusplus */ |
||
3474 |
|||
3475 |
struct mjs; |
||
3476 |
|||
3477 |
typedef void (*gc_cell_destructor_t)(struct mjs *mjs, void *); |
||
3478 |
|||
3479 |
struct gc_block { |
||
3480 |
struct gc_block *next; |
||
3481 |
struct gc_cell *base; |
||
3482 |
size_t size; |
||
3483 |
}; |
||
3484 |
|||
3485 |
struct gc_arena { |
||
3486 |
struct gc_block *blocks; |
||
3487 |
size_t size_increment; |
||
3488 |
struct gc_cell *free; /* head of free list */ |
||
3489 |
size_t cell_size; |
||
3490 |
|||
3491 |
#if MJS_MEMORY_STATS |
||
3492 |
unsigned long allocations; /* cumulative counter of allocations */ |
||
3493 |
unsigned long garbage; /* cumulative counter of garbage */ |
||
3494 |
unsigned long alive; /* number of living cells */ |
||
3495 |
#endif |
||
3496 |
|||
3497 |
gc_cell_destructor_t destructor; |
||
3498 |
}; |
||
3499 |
|||
3500 |
#if defined(__cplusplus) |
||
3501 |
} |
||
3502 |
#endif /* __cplusplus */ |
||
3503 |
|||
3504 |
#endif /* MJS_MM_H_ */ |
||
3505 |
#ifdef MJS_MODULE_LINES |
||
3506 |
#line 1 "mjs/src/mjs_gc.h" |
||
3507 |
#endif |
||
3508 |
/* |
||
3509 |
* Copyright (c) 2014 Cesanta Software Limited |
||
3510 |
* All rights reserved |
||
3511 |
*/ |
||
3512 |
|||
3513 |
#ifndef MJS_GC_H_ |
||
3514 |
#define MJS_GC_H_ |
||
3515 |
|||
3516 |
/* Amalgamated: #include "mjs/src/mjs_core.h" */ |
||
3517 |
/* Amalgamated: #include "mjs/src/mjs_mm.h" */ |
||
3518 |
/* Amalgamated: #include "mjs/src/mjs_internal.h" */ |
||
3519 |
|||
3520 |
#if defined(__cplusplus) |
||
3521 |
extern "C" { |
||
3522 |
#endif /* __cplusplus */ |
||
3523 |
|||
3524 |
/* |
||
3525 |
* performs arithmetics on gc_cell pointers as if they were arena->cell_size |
||
3526 |
* bytes wide |
||
3527 |
*/ |
||
3528 |
#define GC_CELL_OP(arena, cell, op, arg) \ |
||
3529 |
((struct gc_cell *) (((char *) (cell)) op((arg) * (arena)->cell_size))) |
||
3530 |
|||
3531 |
struct gc_cell { |
||
3532 |
union { |
||
3533 |
struct gc_cell *link; |
||
3534 |
uintptr_t word; |
||
3535 |
} head; |
||
3536 |
}; |
||
3537 |
|||
3538 |
/* |
||
3539 |
* Perform garbage collection. |
||
3540 |
* Pass true to full in order to reclaim unused heap back to the OS. |
||
3541 |
*/ |
||
3542 |
void mjs_gc(struct mjs *mjs, int full); |
||
3543 |
|||
3544 |
MJS_PRIVATE int gc_strings_is_gc_needed(struct mjs *mjs); |
||
3545 |
|||
3546 |
/* perform gc if not inhibited */ |
||
3547 |
MJS_PRIVATE int maybe_gc(struct mjs *mjs); |
||
3548 |
|||
3549 |
MJS_PRIVATE struct mjs_object *new_object(struct mjs *); |
||
3550 |
MJS_PRIVATE struct mjs_property *new_property(struct mjs *); |
||
3551 |
MJS_PRIVATE struct mjs_ffi_sig *new_ffi_sig(struct mjs *mjs); |
||
3552 |
|||
3553 |
MJS_PRIVATE void gc_mark(struct mjs *mjs, mjs_val_t *val); |
||
3554 |
|||
3555 |
MJS_PRIVATE void gc_arena_init(struct gc_arena *, size_t, size_t, size_t); |
||
3556 |
MJS_PRIVATE void gc_arena_destroy(struct mjs *, struct gc_arena *a); |
||
3557 |
MJS_PRIVATE void gc_sweep(struct mjs *, struct gc_arena *, size_t); |
||
3558 |
MJS_PRIVATE void *gc_alloc_cell(struct mjs *, struct gc_arena *); |
||
3559 |
|||
3560 |
MJS_PRIVATE uint64_t gc_string_mjs_val_to_offset(mjs_val_t v); |
||
3561 |
|||
3562 |
/* return 0 if v is an object/function with a bad pointer */ |
||
3563 |
MJS_PRIVATE int gc_check_val(struct mjs *mjs, mjs_val_t v); |
||
3564 |
|||
3565 |
/* checks whether a pointer is within the ranges of an arena */ |
||
3566 |
MJS_PRIVATE int gc_check_ptr(const struct gc_arena *a, const void *p); |
||
3567 |
|||
3568 |
#if defined(__cplusplus) |
||
3569 |
} |
||
3570 |
#endif /* __cplusplus */ |
||
3571 |
|||
3572 |
#endif /* MJS_GC_H_ */ |
||
3573 |
#ifdef MJS_MODULE_LINES |
||
3574 |
#line 1 "mjs/src/mjs_core.h" |
||
3575 |
#endif |
||
3576 |
/* |
||
3577 |
* Copyright (c) 2017 Cesanta Software Limited |
||
3578 |
* All rights reserved |
||
3579 |
*/ |
||
3580 |
|||
3581 |
#ifndef MJS_CORE_H |
||
3582 |
#define MJS_CORE_H |
||
3583 |
|||
3584 |
/* Amalgamated: #include "mjs/src/mjs_ffi.h" */ |
||
3585 |
/* Amalgamated: #include "mjs/src/mjs_gc.h" */ |
||
3586 |
/* Amalgamated: #include "mjs/src/mjs_internal.h" */ |
||
3587 |
|||
3588 |
#if defined(__cplusplus) |
||
3589 |
extern "C" { |
||
3590 |
#endif /* __cplusplus */ |
||
3591 |
|||
3592 |
#define JUMP_INSTRUCTION_SIZE 2 |
||
3593 |
|||
3594 |
enum mjs_type { |
||
3595 |
/* Primitive types */ |
||
3596 |
MJS_TYPE_UNDEFINED, |
||
3597 |
MJS_TYPE_NULL, |
||
3598 |
MJS_TYPE_BOOLEAN, |
||
3599 |
MJS_TYPE_NUMBER, |
||
3600 |
MJS_TYPE_STRING, |
||
3601 |
MJS_TYPE_FOREIGN, |
||
3602 |
|||
3603 |
/* Different classes of Object type */ |
||
3604 |
MJS_TYPE_OBJECT_GENERIC, |
||
3605 |
MJS_TYPE_OBJECT_ARRAY, |
||
3606 |
MJS_TYPE_OBJECT_FUNCTION, |
||
3607 |
/* |
||
3608 |
* TODO(dfrank): if we support prototypes, need to add items for them here |
||
3609 |
*/ |
||
3610 |
|||
3611 |
MJS_TYPES_CNT |
||
3612 |
}; |
||
3613 |
|||
3614 |
enum mjs_call_stack_frame_item { |
||
3615 |
CALL_STACK_FRAME_ITEM_RETVAL_STACK_IDX, /* TOS */ |
||
3616 |
CALL_STACK_FRAME_ITEM_LOOP_ADDR_IDX, |
||
3617 |
CALL_STACK_FRAME_ITEM_SCOPE_IDX, |
||
3618 |
CALL_STACK_FRAME_ITEM_RETURN_ADDR, |
||
3619 |
CALL_STACK_FRAME_ITEM_THIS, |
||
3620 |
|||
3621 |
CALL_STACK_FRAME_ITEMS_CNT |
||
3622 |
}; |
||
3623 |
|||
3624 |
/* |
||
3625 |
* A tag is made of the sign bit and the 4 lower order bits of byte 6. |
||
3626 |
* So in total we have 32 possible tags. |
||
3627 |
* |
||
3628 |
* Tag (1,0) however cannot hold a zero payload otherwise it's interpreted as an |
||
3629 |
* INFINITY; for simplicity we're just not going to use that combination. |
||
3630 |
*/ |
||
3631 |
#define MAKE_TAG(s, t) \ |
||
3632 |
((uint64_t)(s) << 63 | (uint64_t) 0x7ff0 << 48 | (uint64_t)(t) << 48) |
||
3633 |
|||
3634 |
#define MJS_TAG_OBJECT MAKE_TAG(1, 1) |
||
3635 |
#define MJS_TAG_FOREIGN MAKE_TAG(1, 2) |
||
3636 |
#define MJS_TAG_UNDEFINED MAKE_TAG(1, 3) |
||
3637 |
#define MJS_TAG_BOOLEAN MAKE_TAG(1, 4) |
||
3638 |
#define MJS_TAG_NAN MAKE_TAG(1, 5) |
||
3639 |
#define MJS_TAG_STRING_I MAKE_TAG(1, 6) /* Inlined string len < 5 */ |
||
3640 |
#define MJS_TAG_STRING_5 MAKE_TAG(1, 7) /* Inlined string len 5 */ |
||
3641 |
#define MJS_TAG_STRING_O MAKE_TAG(1, 8) /* Owned string */ |
||
3642 |
#define MJS_TAG_STRING_F MAKE_TAG(1, 9) /* Foreign string */ |
||
3643 |
#define MJS_TAG_STRING_C MAKE_TAG(1, 10) /* String chunk */ |
||
3644 |
#define MJS_TAG_STRING_D MAKE_TAG(1, 11) /* Dictionary string */ |
||
3645 |
#define MJS_TAG_ARRAY MAKE_TAG(1, 12) |
||
3646 |
#define MJS_TAG_FUNCTION MAKE_TAG(1, 13) |
||
3647 |
#define MJS_TAG_FUNCTION_FFI MAKE_TAG(1, 14) |
||
3648 |
#define MJS_TAG_NULL MAKE_TAG(1, 15) |
||
3649 |
|||
3650 |
#define MJS_TAG_MASK MAKE_TAG(1, 15) |
||
3651 |
|||
3652 |
struct mjs_vals { |
||
3653 |
/* Current `this` value */ |
||
3654 |
mjs_val_t this_obj; |
||
3655 |
mjs_val_t dataview_proto; |
||
3656 |
|||
3657 |
/* |
||
3658 |
* The object against which the last `OP_GET` was invoked. Needed for |
||
3659 |
* "method invocation pattern". |
||
3660 |
*/ |
||
3661 |
mjs_val_t last_getprop_obj; |
||
3662 |
}; |
||
3663 |
|||
3664 |
struct mjs_bcode_part { |
||
3665 |
/* Global index of the bcode part */ |
||
3666 |
size_t start_idx; |
||
3667 |
|||
3668 |
/* Actual bcode data */ |
||
3669 |
struct { |
||
3670 |
const char *p; /* Memory chunk pointer */ |
||
3671 |
size_t len; /* Memory chunk length */ |
||
3672 |
} data; |
||
3673 |
|||
3674 |
/* |
||
3675 |
* Result of evaluation (not parsing: if there is an error during parsing, |
||
3676 |
* the bcode is not even committed). It is used to determine whether we |
||
3677 |
* need to evaluate the file: if file was already evaluated, and the result |
||
3678 |
* was MJS_OK, then we won't evaluate it again. Otherwise, we will. |
||
3679 |
*/ |
||
3680 |
mjs_err_t exec_res : 4; |
||
3681 |
|||
3682 |
/* If set, bcode data does not need to be freed */ |
||
3683 |
unsigned in_rom : 1; |
||
3684 |
}; |
||
3685 |
|||
3686 |
struct mjs { |
||
3687 |
struct mbuf bcode_gen; |
||
3688 |
struct mbuf bcode_parts; |
||
3689 |
size_t bcode_len; |
||
3690 |
struct mbuf stack; |
||
3691 |
struct mbuf call_stack; |
||
3692 |
struct mbuf arg_stack; |
||
3693 |
struct mbuf scopes; /* Scope objects */ |
||
3694 |
struct mbuf loop_addresses; /* Addresses for breaks & continues */ |
||
3695 |
struct mbuf owned_strings; /* Sequence of (varint len, char data[]) */ |
||
3696 |
struct mbuf foreign_strings; /* Sequence of (varint len, char *data) */ |
||
3697 |
struct mbuf owned_values; |
||
3698 |
struct mbuf json_visited_stack; |
||
3699 |
struct mjs_vals vals; |
||
3700 |
char *error_msg; |
||
3701 |
char *stack_trace; |
||
3702 |
enum mjs_err error; |
||
3703 |
mjs_ffi_resolver_t *dlsym; /* Symbol resolver function for FFI */ |
||
3704 |
ffi_cb_args_t *ffi_cb_args; /* List of FFI args descriptors */ |
||
3705 |
size_t cur_bcode_offset; |
||
3706 |
|||
3707 |
struct gc_arena object_arena; |
||
3708 |
struct gc_arena property_arena; |
||
3709 |
struct gc_arena ffi_sig_arena; |
||
3710 |
|||
3711 |
unsigned inhibit_gc : 1; |
||
3712 |
unsigned need_gc : 1; |
||
3713 |
unsigned generate_jsc : 1; |
||
3714 |
}; |
||
3715 |
|||
3716 |
/* |
||
3717 |
* Bcode header: type of the items, and item numbers. |
||
3718 |
*/ |
||
3719 |
typedef uint32_t mjs_header_item_t; |
||
3720 |
enum mjs_header_items { |
||
3721 |
MJS_HDR_ITEM_TOTAL_SIZE, /* Total size of the bcode (not counting the |
||
3722 |
OP_BCODE_HEADER byte) */ |
||
3723 |
MJS_HDR_ITEM_BCODE_OFFSET, /* Offset to the start of the actual bcode (not |
||
3724 |
counting the OP_BCODE_HEADER byte) */ |
||
3725 |
MJS_HDR_ITEM_MAP_OFFSET, /* Offset to the start of offset-to-line_no mapping |
||
3726 |
k*/ |
||
3727 |
|||
3728 |
MJS_HDR_ITEMS_CNT |
||
3729 |
}; |
||
3730 |
|||
3731 |
MJS_PRIVATE size_t mjs_get_func_addr(mjs_val_t v); |
||
3732 |
|||
3733 |
MJS_PRIVATE int mjs_getretvalpos(struct mjs *mjs); |
||
3734 |
|||
3735 |
MJS_PRIVATE enum mjs_type mjs_get_type(mjs_val_t v); |
||
3736 |
|||
3737 |
/* |
||
3738 |
* Prints stack trace starting from the given bcode offset; other offsets |
||
3739 |
* (if any) will be fetched from the call_stack. |
||
3740 |
*/ |
||
3741 |
MJS_PRIVATE void mjs_gen_stack_trace(struct mjs *mjs, size_t offset); |
||
3742 |
|||
3743 |
MJS_PRIVATE mjs_val_t vtop(struct mbuf *m); |
||
3744 |
MJS_PRIVATE size_t mjs_stack_size(const struct mbuf *m); |
||
3745 |
MJS_PRIVATE mjs_val_t *vptr(struct mbuf *m, int idx); |
||
3746 |
MJS_PRIVATE void push_mjs_val(struct mbuf *m, mjs_val_t v); |
||
3747 |
MJS_PRIVATE mjs_val_t mjs_pop_val(struct mbuf *m); |
||
3748 |
MJS_PRIVATE mjs_val_t mjs_pop(struct mjs *mjs); |
||
3749 |
MJS_PRIVATE void mjs_push(struct mjs *mjs, mjs_val_t v); |
||
3750 |
MJS_PRIVATE void mjs_die(struct mjs *mjs); |
||
3751 |
|||
3752 |
#if defined(__cplusplus) |
||
3753 |
} |
||
3754 |
#endif /* __cplusplus */ |
||
3755 |
|||
3756 |
#endif /* MJS_CORE_H */ |
||
3757 |
#ifdef MJS_MODULE_LINES |
||
3758 |
#line 1 "mjs/src/mjs_conversion.h" |
||
3759 |
#endif |
||
3760 |
/* |
||
3761 |
* Copyright (c) 2016 Cesanta Software Limited |
||
3762 |
* All rights reserved |
||
3763 |
*/ |
||
3764 |
|||
3765 |
#ifndef MJS_CONVERSION_H_ |
||
3766 |
#define MJS_CONVERSION_H_ |
||
3767 |
|||
3768 |
/* Amalgamated: #include "mjs/src/mjs_internal.h" */ |
||
3769 |
/* Amalgamated: #include "mjs/src/mjs_core.h" */ |
||
3770 |
|||
3771 |
#if defined(__cplusplus) |
||
3772 |
extern "C" { |
||
3773 |
#endif /* __cplusplus */ |
||
3774 |
|||
3775 |
/* |
||
3776 |
* Tries to convert `mjs_val_t` to a string, returns MJS_OK if successful. |
||
3777 |
* String is returned as a pair of pointers: `char **p, size_t *sizep`. |
||
3778 |
* |
||
3779 |
* Caller must also provide a non-null `need_free`, and if it is non-zero, |
||
3780 |
* then the string `*p` should be freed by the caller. |
||
3781 |
* |
||
3782 |
* MJS does not support `toString()` and `valueOf()`, so, passing an object |
||
3783 |
* always results in `MJS_TYPE_ERROR`. |
||
3784 |
*/ |
||
3785 |
MJS_PRIVATE mjs_err_t mjs_to_string(struct mjs *mjs, mjs_val_t *v, char **p, |
||
3786 |
size_t *sizep, int *need_free); |
||
3787 |
|||
3788 |
/* |
||
3789 |
* Converts value to boolean as in the expression `if (v)`. |
||
3790 |
*/ |
||
3791 |
MJS_PRIVATE mjs_val_t mjs_to_boolean_v(struct mjs *mjs, mjs_val_t v); |
||
3792 |
|||
3793 |
MJS_PRIVATE int mjs_is_truthy(struct mjs *mjs, mjs_val_t v); |
||
3794 |
|||
3795 |
#if defined(__cplusplus) |
||
3796 |
} |
||
3797 |
#endif /* __cplusplus */ |
||
3798 |
|||
3799 |
#endif /* MJS_CONVERSION_H_ */ |
||
3800 |
#ifdef MJS_MODULE_LINES |
||
3801 |
#line 1 "mjs/src/mjs_object_public.h" |
||
3802 |
#endif |
||
3803 |
/* |
||
3804 |
* Copyright (c) 2016 Cesanta Software Limited |
||
3805 |
* All rights reserved |
||
3806 |
*/ |
||
3807 |
|||
3808 |
#ifndef MJS_OBJECT_PUBLIC_H_ |
||
3809 |
#define MJS_OBJECT_PUBLIC_H_ |
||
3810 |
|||
3811 |
#include <stddef.h> |
||
3812 |
/* Amalgamated: #include "mjs/src/mjs_core_public.h" */ |
||
3813 |
/* Amalgamated: #include "mjs/src/mjs_ffi_public.h" */ |
||
3814 |
|||
3815 |
#if defined(__cplusplus) |
||
3816 |
extern "C" { |
||
3817 |
#endif /* __cplusplus */ |
||
3818 |
|||
3819 |
/* |
||
3820 |
* Returns true if the given value is an object or array. |
||
3821 |
*/ |
||
3822 |
int mjs_is_object(mjs_val_t v); |
||
3823 |
|||
3824 |
/* Make an empty object */ |
||
3825 |
mjs_val_t mjs_mk_object(struct mjs *mjs); |
||
3826 |
|||
3827 |
/* C structure layout descriptor - needed by mjs_struct_to_obj */ |
||
3828 |
struct mjs_c_struct_member { |
||
3829 |
const char *name; |
||
3830 |
size_t offset; |
||
3831 |
enum mjs_ffi_ctype type; |
||
3832 |
}; |
||
3833 |
|||
3834 |
/* Create flat JS object from a C memory descriptor */ |
||
3835 |
mjs_val_t mjs_struct_to_obj(struct mjs *mjs, const void *base, |
||
3836 |
const struct mjs_c_struct_member *members); |
||
3837 |
|||
3838 |
/* |
||
3839 |
* Lookup property `name` in object `obj`. If `obj` holds no such property, |
||
3840 |
* an `undefined` value is returned. |
||
3841 |
* |
||
3842 |
* If `name_len` is ~0, `name` is assumed to be NUL-terminated and |
||
3843 |
* `strlen(name)` is used. |
||
3844 |
*/ |
||
3845 |
mjs_val_t mjs_get(struct mjs *mjs, mjs_val_t obj, const char *name, |
||
3846 |
size_t name_len); |
||
3847 |
|||
3848 |
/* |
||
3849 |
* Like mjs_get but with a JS string. |
||
3850 |
*/ |
||
3851 |
mjs_val_t mjs_get_v(struct mjs *mjs, mjs_val_t obj, mjs_val_t name); |
||
3852 |
|||
3853 |
/* |
||
3854 |
* Like mjs_get_v but lookup the prototype chain. |
||
3855 |
*/ |
||
3856 |
mjs_val_t mjs_get_v_proto(struct mjs *mjs, mjs_val_t obj, mjs_val_t key); |
||
3857 |
|||
3858 |
/* |
||
3859 |
* Set object property. Behaves just like JavaScript assignment. |
||
3860 |
*/ |
||
3861 |
mjs_err_t mjs_set(struct mjs *mjs, mjs_val_t obj, const char *name, size_t len, |
||
3862 |
mjs_val_t val); |
||
3863 |
|||
3864 |
/* |
||
3865 |
* Like mjs_set but the name is already a JS string. |
||
3866 |
*/ |
||
3867 |
mjs_err_t mjs_set_v(struct mjs *mjs, mjs_val_t obj, mjs_val_t name, |
||
3868 |
mjs_val_t val); |
||
3869 |
|||
3870 |
/* |
||
3871 |
* Delete own property `name` of the object `obj`. Does not follow the |
||
3872 |
* prototype chain. |
||
3873 |
* |
||
3874 |
* If `name_len` is ~0, `name` is assumed to be NUL-terminated and |
||
3875 |
* `strlen(name)` is used. |
||
3876 |
* |
||
3877 |
* Returns 0 on success, -1 on error. |
||
3878 |
*/ |
||
3879 |
int mjs_del(struct mjs *mjs, mjs_val_t obj, const char *name, size_t len); |
||
3880 |
|||
3881 |
/* |
||
3882 |
* Iterate over `obj` properties. |
||
3883 |
* First call should set `iterator` to MJS_UNDEFINED. |
||
3884 |
* Return object's key (a string), or MJS_UNDEFINED when no more keys left. |
||
3885 |
* Do not mutate the object during iteration. |
||
3886 |
* |
||
3887 |
* Example: |
||
3888 |
* mjs_val_t key, iter = MJS_UNDEFINED; |
||
3889 |
* while ((key = mjs_next(mjs, obj, &iter)) != MJS_UNDEFINED) { |
||
3890 |
* // Do something with the obj/key ... |
||
3891 |
* } |
||
3892 |
*/ |
||
3893 |
mjs_val_t mjs_next(struct mjs *mjs, mjs_val_t obj, mjs_val_t *iterator); |
||
3894 |
|||
3895 |
#if defined(__cplusplus) |
||
3896 |
} |
||
3897 |
#endif /* __cplusplus */ |
||
3898 |
|||
3899 |
#endif /* MJS_OBJECT_PUBLIC_H_ */ |
||
3900 |
#ifdef MJS_MODULE_LINES |
||
3901 |
#line 1 "mjs/src/mjs_object.h" |
||
3902 |
#endif |
||
3903 |
/* |
||
3904 |
* Copyright (c) 2016 Cesanta Software Limited |
||
3905 |
* All rights reserved |
||
3906 |
*/ |
||
3907 |
|||
3908 |
#ifndef MJS_OBJECT_H_ |
||
3909 |
#define MJS_OBJECT_H_ |
||
3910 |
|||
3911 |
/* Amalgamated: #include "mjs/src/mjs_object_public.h" */ |
||
3912 |
/* Amalgamated: #include "mjs/src/mjs_internal.h" */ |
||
3913 |
|||
3914 |
#if defined(__cplusplus) |
||
3915 |
extern "C" { |
||
3916 |
#endif /* __cplusplus */ |
||
3917 |
|||
3918 |
struct mjs; |
||
3919 |
|||
3920 |
struct mjs_property { |
||
3921 |
struct mjs_property *next; /* Linkage in struct mjs_object::properties */ |
||
3922 |
mjs_val_t name; /* Property name (a string) */ |
||
3923 |
mjs_val_t value; /* Property value */ |
||
3924 |
}; |
||
3925 |
|||
3926 |
struct mjs_object { |
||
3927 |
struct mjs_property *properties; |
||
3928 |
}; |
||
3929 |
|||
3930 |
MJS_PRIVATE struct mjs_object *get_object_struct(mjs_val_t v); |
||
3931 |
MJS_PRIVATE struct mjs_property *mjs_get_own_property(struct mjs *mjs, |
||
3932 |
mjs_val_t obj, |
||
3933 |
const char *name, |
||
3934 |
size_t len); |
||
3935 |
|||
3936 |
MJS_PRIVATE struct mjs_property *mjs_get_own_property_v(struct mjs *mjs, |
||
3937 |
mjs_val_t obj, |
||
3938 |
mjs_val_t key); |
||
3939 |
|||
3940 |
/* |
||
3941 |
* A worker function for `mjs_set()` and `mjs_set_v()`: it takes name as both |
||
3942 |
* ptr+len and mjs_val_t. If `name` pointer is not NULL, it takes precedence |
||
3943 |
* over `name_v`. |
||
3944 |
*/ |
||
3945 |
MJS_PRIVATE mjs_err_t mjs_set_internal(struct mjs *mjs, mjs_val_t obj, |
||
3946 |
mjs_val_t name_v, char *name, |
||
3947 |
size_t name_len, mjs_val_t val); |
||
3948 |
|||
3949 |
/* |
||
3950 |
* Implementation of `Object.create(proto)` |
||
3951 |
*/ |
||
3952 |
MJS_PRIVATE void mjs_op_create_object(struct mjs *mjs); |
||
3953 |
|||
3954 |
#define MJS_PROTO_PROP_NAME "__p" /* Make it < 5 chars */ |
||
3955 |
|||
3956 |
#if defined(__cplusplus) |
||
3957 |
} |
||
3958 |
#endif /* __cplusplus */ |
||
3959 |
|||
3960 |
#endif /* MJS_OBJECT_H_ */ |
||
3961 |
#ifdef MJS_MODULE_LINES |
||
3962 |
#line 1 "mjs/src/mjs_primitive_public.h" |
||
3963 |
#endif |
||
3964 |
/* |
||
3965 |
* Copyright (c) 2016 Cesanta Software Limited |
||
3966 |
* All rights reserved |
||
3967 |
*/ |
||
3968 |
|||
3969 |
#ifndef MJS_PRIMITIVE_PUBLIC_H_ |
||
3970 |
#define MJS_PRIMITIVE_PUBLIC_H_ |
||
3971 |
|||
3972 |
/* Amalgamated: #include "mjs/src/mjs_core_public.h" */ |
||
3973 |
|||
3974 |
#if defined(__cplusplus) |
||
3975 |
extern "C" { |
||
3976 |
#endif /* __cplusplus */ |
||
3977 |
|||
3978 |
/* JavaScript `null` value */ |
||
3979 |
#define MJS_NULL MJS_TAG_NULL |
||
3980 |
|||
3981 |
/* JavaScript `undefined` value */ |
||
3982 |
#define MJS_UNDEFINED MJS_TAG_UNDEFINED |
||
3983 |
|||
3984 |
/* Function pointer type used in `mjs_mk_foreign_func`. */ |
||
3985 |
typedef void (*mjs_func_ptr_t)(void); |
||
3986 |
|||
3987 |
/* |
||
3988 |
* Make `null` primitive value. |
||
3989 |
* |
||
3990 |
* NOTE: this function is deprecated and will be removed in future releases. |
||
3991 |
* Use `MJS_NULL` instead. |
||
3992 |
*/ |
||
3993 |
mjs_val_t mjs_mk_null(void); |
||
3994 |
|||
3995 |
/* Returns true if given value is a primitive `null` value */ |
||
3996 |
int mjs_is_null(mjs_val_t v); |
||
3997 |
|||
3998 |
/* |
||
3999 |
* Make `undefined` primitive value. |
||
4000 |
* |
||
4001 |
* NOTE: this function is deprecated and will be removed in future releases. |
||
4002 |
* Use `MJS_UNDEFINED` instead. |
||
4003 |
*/ |
||
4004 |
mjs_val_t mjs_mk_undefined(void); |
||
4005 |
|||
4006 |
/* Returns true if given value is a primitive `undefined` value */ |
||
4007 |
int mjs_is_undefined(mjs_val_t v); |
||
4008 |
|||
4009 |
/* Make numeric primitive value */ |
||
4010 |
mjs_val_t mjs_mk_number(struct mjs *mjs, double num); |
||
4011 |
|||
4012 |
/* |
||
4013 |
* Returns number value stored in `mjs_val_t` as `double`. |
||
4014 |
* |
||
4015 |
* Returns NaN for non-numbers. |
||
4016 |
*/ |
||
4017 |
double mjs_get_double(struct mjs *mjs, mjs_val_t v); |
||
4018 |
|||
4019 |
/* |
||
4020 |
* Returns number value stored in `mjs_val_t` as `int`. If the number value is |
||
4021 |
* not an integer, the fraction part will be discarded. |
||
4022 |
* |
||
4023 |
* If the given value is a non-number, or NaN, the result is undefined. |
||
4024 |
*/ |
||
4025 |
int mjs_get_int(struct mjs *mjs, mjs_val_t v); |
||
4026 |
|||
4027 |
/* |
||
4028 |
* Like mjs_get_int but ensures that the returned type |
||
4029 |
* is a 32-bit signed integer. |
||
4030 |
*/ |
||
4031 |
int32_t mjs_get_int32(struct mjs *mjs, mjs_val_t v); |
||
4032 |
|||
4033 |
/* Returns true if given value is a primitive number value */ |
||
4034 |
int mjs_is_number(mjs_val_t v); |
||
4035 |
|||
4036 |
/* |
||
4037 |
* Make JavaScript value that holds C/C++ `void *` pointer. |
||
4038 |
* |
||
4039 |
* A foreign value is completely opaque and JS code cannot do anything useful |
||
4040 |
* with it except holding it in properties and passing it around. |
||
4041 |
* It behaves like a sealed object with no properties. |
||
4042 |
* |
||
4043 |
* NOTE: |
||
4044 |
* Only valid pointers (as defined by each supported architecture) will fully |
||
4045 |
* preserved. In particular, all supported 64-bit architectures (x86_64, ARM-64) |
||
4046 |
* actually define a 48-bit virtual address space. |
||
4047 |
* Foreign values will be sign-extended as required, i.e creating a foreign |
||
4048 |
* value of something like `(void *) -1` will work as expected. This is |
||
4049 |
* important because in some 64-bit OSs (e.g. Solaris) the user stack grows |
||
4050 |
* downwards from the end of the address space. |
||
4051 |
* |
||
4052 |
* If you need to store exactly sizeof(void*) bytes of raw data where |
||
4053 |
* `sizeof(void*)` >= 8, please use byte arrays instead. |
||
4054 |
*/ |
||
4055 |
mjs_val_t mjs_mk_foreign(struct mjs *mjs, void *ptr); |
||
4056 |
|||
4057 |
/* |
||
4058 |
* Make JavaScript value that holds C/C++ function pointer, similarly to |
||
4059 |
* `mjs_mk_foreign`. |
||
4060 |
*/ |
||
4061 |
mjs_val_t mjs_mk_foreign_func(struct mjs *mjs, mjs_func_ptr_t fn); |
||
4062 |
|||
4063 |
/* |
||
4064 |
* Returns `void *` pointer stored in `mjs_val_t`. |
||
4065 |
* |
||
4066 |
* Returns NULL if the value is not a foreign pointer. |
||
4067 |
*/ |
||
4068 |
void *mjs_get_ptr(struct mjs *mjs, mjs_val_t v); |
||
4069 |
|||
4070 |
/* Returns true if given value holds `void *` pointer */ |
||
4071 |
int mjs_is_foreign(mjs_val_t v); |
||
4072 |
|||
4073 |
mjs_val_t mjs_mk_boolean(struct mjs *mjs, int v); |
||
4074 |
int mjs_get_bool(struct mjs *mjs, mjs_val_t v); |
||
4075 |
int mjs_is_boolean(mjs_val_t v); |
||
4076 |
|||
4077 |
mjs_val_t mjs_mk_function(struct mjs *mjs, size_t off); |
||
4078 |
int mjs_is_function(mjs_val_t v); |
||
4079 |
|||
4080 |
#if defined(__cplusplus) |
||
4081 |
} |
||
4082 |
#endif /* __cplusplus */ |
||
4083 |
|||
4084 |
#endif /* MJS_PRIMITIVE_PUBLIC_H_ */ |
||
4085 |
#ifdef MJS_MODULE_LINES |
||
4086 |
#line 1 "mjs/src/mjs_primitive.h" |
||
4087 |
#endif |
||
4088 |
/* |
||
4089 |
* Copyright (c) 2016 Cesanta Software Limited |
||
4090 |
* All rights reserved |
||
4091 |
*/ |
||
4092 |
|||
4093 |
#ifndef MJS_PRIMITIVE_H |
||
4094 |
#define MJS_PRIMITIVE_H |
||
4095 |
|||
4096 |
/* Amalgamated: #include "mjs/src/mjs_primitive_public.h" */ |
||
4097 |
/* Amalgamated: #include "mjs/src/mjs_internal.h" */ |
||
4098 |
|||
4099 |
#if defined(__cplusplus) |
||
4100 |
extern "C" { |
||
4101 |
#endif /* __cplusplus */ |
||
4102 |
|||
4103 |
/* |
||
4104 |
* Convert a pointer to mjs_val_t. If pointer is not valid, mjs crashes. |
||
4105 |
*/ |
||
4106 |
MJS_PRIVATE mjs_val_t mjs_legit_pointer_to_value(void *p); |
||
4107 |
|||
4108 |
/* |
||
4109 |
* Convert a pointer to mjs_val_t. If pointer is not valid, error is set |
||
4110 |
* in the mjs context. |
||
4111 |
*/ |
||
4112 |
MJS_PRIVATE mjs_val_t mjs_pointer_to_value(struct mjs *mjs, void *p); |
||
4113 |
|||
4114 |
/* |
||
4115 |
* Extracts a pointer from the mjs_val_t value. |
||
4116 |
*/ |
||
4117 |
MJS_PRIVATE void *get_ptr(mjs_val_t v); |
||
4118 |
|||
4119 |
/* |
||
4120 |
* Implementation for JS isNaN() |
||
4121 |
*/ |
||
4122 |
MJS_PRIVATE void mjs_op_isnan(struct mjs *mjs); |
||
4123 |
|||
4124 |
#if defined(__cplusplus) |
||
4125 |
} |
||
4126 |
#endif /* __cplusplus */ |
||
4127 |
|||
4128 |
#endif /* MJS_PRIMITIVE_H */ |
||
4129 |
#ifdef MJS_MODULE_LINES |
||
4130 |
#line 1 "mjs/src/mjs_string_public.h" |
||
4131 |
#endif |
||
4132 |
/* |
||
4133 |
* Copyright (c) 2016 Cesanta Software Limited |
||
4134 |
* All rights reserved |
||
4135 |
*/ |
||
4136 |
|||
4137 |
#ifndef MJS_STRING_PUBLIC_H_ |
||
4138 |
#define MJS_STRING_PUBLIC_H_ |
||
4139 |
|||
4140 |
/* Amalgamated: #include "mjs/src/mjs_core_public.h" */ |
||
4141 |
|||
4142 |
#define MJS_STRING_LITERAL_MAX_LEN 128 |
||
4143 |
|||
4144 |
#if defined(__cplusplus) |
||
4145 |
extern "C" { |
||
4146 |
#endif /* __cplusplus */ |
||
4147 |
|||
4148 |
/* |
||
4149 |
* Creates a string primitive value. |
||
4150 |
* `str` must point to the utf8 string of length `len`. |
||
4151 |
* If `len` is ~0, `str` is assumed to be NUL-terminated and `strlen(str)` is |
||
4152 |
* used. |
||
4153 |
* |
||
4154 |
* If `copy` is non-zero, the string data is copied and owned by the GC. The |
||
4155 |
* caller can free the string data afterwards. Otherwise (`copy` is zero), the |
||
4156 |
* caller owns the string data, and is responsible for not freeing it while it |
||
4157 |
* is used. |
||
4158 |
*/ |
||
4159 |
mjs_val_t mjs_mk_string(struct mjs *mjs, const char *str, size_t len, int copy); |
||
4160 |
|||
4161 |
/* Returns true if given value is a primitive string value */ |
||
4162 |
int mjs_is_string(mjs_val_t v); |
||
4163 |
|||
4164 |
/* |
||
4165 |
* Returns a pointer to the string stored in `mjs_val_t`. |
||
4166 |
* |
||
4167 |
* String length returned in `len`, which is allowed to be NULL. Returns NULL |
||
4168 |
* if the value is not a string. |
||
4169 |
* |
||
4170 |
* JS strings can contain embedded NUL chars and may or may not be NUL |
||
4171 |
* terminated. |
||
4172 |
* |
||
4173 |
* CAUTION: creating new JavaScript object, array, or string may kick in a |
||
4174 |
* garbage collector, which in turn may relocate string data and invalidate |
||
4175 |
* pointer returned by `mjs_get_string()`. |
||
4176 |
* |
||
4177 |
* Short JS strings are embedded inside the `mjs_val_t` value itself. This |
||
4178 |
* is why a pointer to a `mjs_val_t` is required. It also means that the string |
||
4179 |
* data will become invalid once that `mjs_val_t` value goes out of scope. |
||
4180 |
*/ |
||
4181 |
const char *mjs_get_string(struct mjs *mjs, mjs_val_t *v, size_t *len); |
||
4182 |
|||
4183 |
/* |
||
4184 |
* Returns a pointer to the string stored in `mjs_val_t`. |
||
4185 |
* |
||
4186 |
* Returns NULL if the value is not a string or if the string is not compatible |
||
4187 |
* with a C string. |
||
4188 |
* |
||
4189 |
* C compatible strings contain exactly one NUL char, in terminal position. |
||
4190 |
* |
||
4191 |
* All strings owned by the MJS engine (see `mjs_mk_string()`) are guaranteed to |
||
4192 |
* be NUL terminated. Out of these, those that don't include embedded NUL chars |
||
4193 |
* are guaranteed to be C compatible. |
||
4194 |
*/ |
||
4195 |
const char *mjs_get_cstring(struct mjs *mjs, mjs_val_t *v); |
||
4196 |
|||
4197 |
/* |
||
4198 |
* Returns the standard strcmp comparison code after comparing a JS string a |
||
4199 |
* with a possibly non null-terminated string b. NOTE: the strings are equal |
||
4200 |
* only if their length is equal, i.e. the len field doesn't imply strncmp |
||
4201 |
* behaviour. |
||
4202 |
*/ |
||
4203 |
int mjs_strcmp(struct mjs *mjs, mjs_val_t *a, const char *b, size_t len); |
||
4204 |
|||
4205 |
#if defined(__cplusplus) |
||
4206 |
} |
||
4207 |
#endif /* __cplusplus */ |
||
4208 |
|||
4209 |
#endif /* MJS_STRING_PUBLIC_H_ */ |
||
4210 |
#ifdef MJS_MODULE_LINES |
||
4211 |
#line 1 "mjs/src/mjs_string.h" |
||
4212 |
#endif |
||
4213 |
/* |
||
4214 |
* Copyright (c) 2016 Cesanta Software Limited |
||
4215 |
* All rights reserved |
||
4216 |
*/ |
||
4217 |
|||
4218 |
#ifndef MJS_STRING_H_ |
||
4219 |
#define MJS_STRING_H_ |
||
4220 |
|||
4221 |
/* Amalgamated: #include "mjs/src/mjs_internal.h" */ |
||
4222 |
/* Amalgamated: #include "mjs/src/mjs_string_public.h" */ |
||
4223 |
|||
4224 |
#if defined(__cplusplus) |
||
4225 |
extern "C" { |
||
4226 |
#endif /* __cplusplus */ |
||
4227 |
|||
4228 |
/* |
||
4229 |
* Size of the extra space for strings mbuf that is needed to avoid frequent |
||
4230 |
* reallocations |
||
4231 |
*/ |
||
4232 |
#define _MJS_STRING_BUF_RESERVE 100 |
||
4233 |
|||
4234 |
MJS_PRIVATE unsigned long cstr_to_ulong(const char *s, size_t len, int *ok); |
||
4235 |
MJS_PRIVATE mjs_err_t |
||
4236 |
str_to_ulong(struct mjs *mjs, mjs_val_t v, int *ok, unsigned long *res); |
||
4237 |
MJS_PRIVATE int s_cmp(struct mjs *mjs, mjs_val_t a, mjs_val_t b); |
||
4238 |
MJS_PRIVATE mjs_val_t s_concat(struct mjs *mjs, mjs_val_t a, mjs_val_t b); |
||
4239 |
|||
4240 |
MJS_PRIVATE void embed_string(struct mbuf *m, size_t offset, const char *p, |
||
4241 |
size_t len, uint8_t /*enum embstr_flags*/ flags); |
||
4242 |
|||
4243 |
MJS_PRIVATE void mjs_mkstr(struct mjs *mjs); |
||
4244 |
|||
4245 |
MJS_PRIVATE void mjs_string_slice(struct mjs *mjs); |
||
4246 |
MJS_PRIVATE void mjs_string_index_of(struct mjs *mjs); |
||
4247 |
MJS_PRIVATE void mjs_string_char_code_at(struct mjs *mjs); |
||
4248 |
|||
4249 |
#define EMBSTR_ZERO_TERM 1 |
||
4250 |
#define EMBSTR_UNESCAPE 2 |
||
4251 |
|||
4252 |
#if defined(__cplusplus) |
||
4253 |
} |
||
4254 |
#endif /* __cplusplus */ |
||
4255 |
|||
4256 |
#endif /* MJS_STRING_H_ */ |
||
4257 |
#ifdef MJS_MODULE_LINES |
||
4258 |
#line 1 "mjs/src/mjs_util_public.h" |
||
4259 |
#endif |
||
4260 |
/* |
||
4261 |
* Copyright (c) 2016 Cesanta Software Limited |
||
4262 |
* All rights reserved |
||
4263 |
*/ |
||
4264 |
|||
4265 |
#ifndef MJS_UTIL_PUBLIC_H_ |
||
4266 |
#define MJS_UTIL_PUBLIC_H_ |
||
4267 |
|||
4268 |
/* Amalgamated: #include "mjs/src/mjs_core_public.h" */ |
||
4269 |
#include <stdio.h> |
||
4270 |
|||
4271 |
#if defined(__cplusplus) |
||
4272 |
extern "C" { |
||
4273 |
#endif /* __cplusplus */ |
||
4274 |
|||
4275 |
const char *mjs_typeof(mjs_val_t v); |
||
4276 |
|||
4277 |
void mjs_fprintf(mjs_val_t v, struct mjs *mjs, FILE *fp); |
||
4278 |
void mjs_sprintf(mjs_val_t v, struct mjs *mjs, char *buf, size_t buflen); |
||
4279 |
|||
4280 |
#if MJS_ENABLE_DEBUG |
||
4281 |
|||
4282 |
void mjs_disasm(const uint8_t *code, size_t len); |
||
4283 |
void mjs_dump(struct mjs *mjs, int do_disasm); |
||
4284 |
|||
4285 |
#endif |
||
4286 |
|||
4287 |
/* |
||
4288 |
* Returns the filename corresponding to the given bcode offset. |
||
4289 |
*/ |
||
4290 |
const char *mjs_get_bcode_filename_by_offset(struct mjs *mjs, int offset); |
||
4291 |
|||
4292 |
/* |
||
4293 |
* Returns the line number corresponding to the given bcode offset. |
||
4294 |
*/ |
||
4295 |
int mjs_get_lineno_by_offset(struct mjs *mjs, int offset); |
||
4296 |
|||
4297 |
/* |
||
4298 |
* Returns bcode offset of the corresponding call frame cf_num, where 0 means |
||
4299 |
* the currently executing function, 1 means the first return address, etc. |
||
4300 |
* |
||
4301 |
* If given cf_num is too large, -1 is returned. |
||
4302 |
*/ |
||
4303 |
int mjs_get_offset_by_call_frame_num(struct mjs *mjs, int cf_num); |
||
4304 |
|||
4305 |
#if defined(__cplusplus) |
||
4306 |
} |
||
4307 |
#endif /* __cplusplus */ |
||
4308 |
|||
4309 |
#endif /* MJS_UTIL_PUBLIC_H_ */ |
||
4310 |
#ifdef MJS_MODULE_LINES |
||
4311 |
#line 1 "mjs/src/mjs_util.h" |
||
4312 |
#endif |
||
4313 |
/* |
||
4314 |
* Copyright (c) 2016 Cesanta Software Limited |
||
4315 |
* All rights reserved |
||
4316 |
*/ |
||
4317 |
|||
4318 |
#ifndef MJS_UTIL_H_ |
||
4319 |
#define MJS_UTIL_H_ |
||
4320 |
|||
4321 |
/* Amalgamated: #include "frozen.h" */ |
||
4322 |
/* Amalgamated: #include "mjs/src/mjs_core.h" */ |
||
4323 |
/* Amalgamated: #include "mjs/src/mjs_util_public.h" */ |
||
4324 |
|||
4325 |
#if defined(__cplusplus) |
||
4326 |
extern "C" { |
||
4327 |
#endif /* __cplusplus */ |
||
4328 |
|||
4329 |
struct mjs_bcode_part; |
||
4330 |
|||
4331 |
MJS_PRIVATE const char *opcodetostr(uint8_t opcode); |
||
4332 |
MJS_PRIVATE size_t mjs_disasm_single(const uint8_t *code, size_t i); |
||
4333 |
MJS_PRIVATE const char *mjs_stringify_type(enum mjs_type t); |
||
4334 |
|||
4335 |
/* |
||
4336 |
* Checks that the given argument is provided, and checks its type. If check |
||
4337 |
* fails, sets error in the mjs context, and returns 0; otherwise returns 1. |
||
4338 |
* |
||
4339 |
* If `arg_num` >= 0, checks argument; otherwise (`arg_num` is negative) checks |
||
4340 |
* `this`. `arg_name` is used for the error message only. If `parg` is not |
||
4341 |
* NULL, writes resulting value at this location in case of success. |
||
4342 |
*/ |
||
4343 |
MJS_PRIVATE int mjs_check_arg(struct mjs *mjs, int arg_num, |
||
4344 |
const char *arg_name, enum mjs_type expected_type, |
||
4345 |
mjs_val_t *parg); |
||
4346 |
|||
4347 |
/* |
||
4348 |
* mjs_normalize_idx takes and index in the string and the string size, and |
||
4349 |
* returns the index which is >= 0 and <= size. Negative index is interpreted |
||
4350 |
* as size + index. |
||
4351 |
*/ |
||
4352 |
MJS_PRIVATE int mjs_normalize_idx(int idx, int size); |
||
4353 |
|||
4354 |
MJS_PRIVATE const char *mjs_get_bcode_filename(struct mjs *mjs, |
||
4355 |
struct mjs_bcode_part *bp); |
||
4356 |
|||
4357 |
/* Print JS value `v` to the JSON stream `out`. */ |
||
4358 |
void mjs_jprintf(mjs_val_t v, struct mjs *mjs, struct json_out *out); |
||
4359 |
|||
4360 |
#if defined(__cplusplus) |
||
4361 |
} |
||
4362 |
#endif /* __cplusplus */ |
||
4363 |
|||
4364 |
#endif /* MJS_UTIL_H_ */ |
||
4365 |
#ifdef MJS_MODULE_LINES |
||
4366 |
#line 1 "common/cs_varint.h" |
||
4367 |
#endif |
||
4368 |
/* |
||
4369 |
* Copyright (c) 2014-2018 Cesanta Software Limited |
||
4370 |
* All rights reserved |
||
4371 |
* |
||
4372 |
* Licensed under the Apache License, Version 2.0 (the ""License""); |
||
4373 |
* you may not use this file except in compliance with the License. |
||
4374 |
* You may obtain a copy of the License at |
||
4375 |
* |
||
4376 |
* http://www.apache.org/licenses/LICENSE-2.0 |
||
4377 |
* |
||
4378 |
* Unless required by applicable law or agreed to in writing, software |
||
4379 |
* distributed under the License is distributed on an ""AS IS"" BASIS, |
||
4380 |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||
4381 |
* See the License for the specific language governing permissions and |
||
4382 |
* limitations under the License. |
||
4383 |
*/ |
||
4384 |
|||
4385 |
#ifndef CS_COMMON_CS_VARINT_H_ |
||
4386 |
#define CS_COMMON_CS_VARINT_H_ |
||
4387 |
|||
4388 |
#if defined(_WIN32) && _MSC_VER < 1700 |
||
4389 |
typedef unsigned char uint8_t; |
||
4390 |
typedef unsigned __int64 uint64_t; |
||
4391 |
#else |
||
4392 |
#include <stdbool.h> |
||
4393 |
#include <stdint.h> |
||
4394 |
#include <stdlib.h> |
||
4395 |
#endif |
||
4396 |
|||
4397 |
#ifdef __cplusplus |
||
4398 |
extern "C" { |
||
4399 |
#endif |
||
4400 |
|||
4401 |
/* Returns number of bytes required to encode `num`. */ |
||
4402 |
size_t cs_varint_llen(uint64_t num); |
||
4403 |
|||
4404 |
/* |
||
4405 |
* Encodes `num` into `buf`. |
||
4406 |
* Returns number of bytes required to encode `num`. |
||
4407 |
* Note: return value may be greater than `buf_size` but the function will only |
||
4408 |
* write `buf_size` bytes. |
||
4409 |
*/ |
||
4410 |
size_t cs_varint_encode(uint64_t num, uint8_t *buf, size_t buf_size); |
||
4411 |
|||
4412 |
/* |
||
4413 |
* Decodes varint stored in `buf`. |
||
4414 |
* Stores the number of bytes consumed into `llen`. |
||
4415 |
* If there aren't enough bytes in `buf` to decode a number, returns false. |
||
4416 |
*/ |
||
4417 |
bool cs_varint_decode(const uint8_t *buf, size_t buf_size, uint64_t *num, |
||
4418 |
size_t *llen); |
||
4419 |
|||
4420 |
uint64_t cs_varint_decode_unsafe(const uint8_t *buf, int *llen); |
||
4421 |
|||
4422 |
#ifdef __cplusplus |
||
4423 |
} |
||
4424 |
#endif |
||
4425 |
|||
4426 |
#endif /* CS_COMMON_CS_VARINT_H_ */ |
||
4427 |
#ifdef MJS_MODULE_LINES |
||
4428 |
#line 1 "mjs/src/mjs_bcode.h" |
||
4429 |
#endif |
||
4430 |
/* |
||
4431 |
* Copyright (c) 2017 Cesanta Software Limited |
||
4432 |
* All rights reserved |
||
4433 |
*/ |
||
4434 |
|||
4435 |
#ifndef MJS_BCODE_H_ |
||
4436 |
#define MJS_BCODE_H_ |
||
4437 |
|||
4438 |
/* Amalgamated: #include "mjs/src/mjs_internal.h" */ |
||
4439 |
|||
4440 |
/* Amalgamated: #include "mjs/src/mjs_core.h" */ |
||
4441 |
|||
4442 |
#if defined(__cplusplus) |
||
4443 |
extern "C" { |
||
4444 |
#endif /* __cplusplus */ |
||
4445 |
|||
4446 |
enum mjs_opcode { |
||
4447 |
OP_NOP, /* ( -- ) */ |
||
4448 |
OP_DROP, /* ( a -- ) */ |
||
4449 |
OP_DUP, /* ( a -- a a ) */ |
||
4450 |
OP_SWAP, /* ( a b -- b a ) */ |
||
4451 |
OP_JMP, /* ( -- ) */ |
||
4452 |
OP_JMP_TRUE, /* ( -- ) */ |
||
4453 |
OP_JMP_NEUTRAL_TRUE, /* ( -- ) */ |
||
4454 |
OP_JMP_FALSE, /* ( -- ) */ |
||
4455 |
OP_JMP_NEUTRAL_FALSE, /* ( -- ) */ |
||
4456 |
OP_FIND_SCOPE, /* ( a -- a b ) */ |
||
4457 |
OP_PUSH_SCOPE, /* ( -- a ) */ |
||
4458 |
OP_PUSH_STR, /* ( -- a ) */ |
||
4459 |
OP_PUSH_TRUE, /* ( -- a ) */ |
||
4460 |
OP_PUSH_FALSE, /* ( -- a ) */ |
||
4461 |
OP_PUSH_INT, /* ( -- a ) */ |
||
4462 |
OP_PUSH_DBL, /* ( -- a ) */ |
||
4463 |
OP_PUSH_NULL, /* ( -- a ) */ |
||
4464 |
OP_PUSH_UNDEF, /* ( -- a ) */ |
||
4465 |
OP_PUSH_OBJ, /* ( -- a ) */ |
||
4466 |
OP_PUSH_ARRAY, /* ( -- a ) */ |
||
4467 |
OP_PUSH_FUNC, /* ( -- a ) */ |
||
4468 |
OP_PUSH_THIS, /* ( -- a ) */ |
||
4469 |
OP_GET, /* ( key obj -- obj[key] ) */ |
||
4470 |
OP_CREATE, /* ( key obj -- ) */ |
||
4471 |
OP_EXPR, /* ( ... -- a ) */ |
||
4472 |
OP_APPEND, /* ( a b -- ) */ |
||
4473 |
OP_SET_ARG, /* ( a -- a ) */ |
||
4474 |
OP_NEW_SCOPE, /* ( -- ) */ |
||
4475 |
OP_DEL_SCOPE, /* ( -- ) */ |
||
4476 |
OP_CALL, /* ( func param1 param2 ... num_params -- result ) */ |
||
4477 |
OP_RETURN, /* ( -- ) */ |
||
4478 |
OP_LOOP, /* ( -- ) Push break & continue addresses to loop_labels */ |
||
4479 |
OP_BREAK, /* ( -- ) */ |
||
4480 |
OP_CONTINUE, /* ( -- ) */ |
||
4481 |
OP_SETRETVAL, /* ( a -- ) */ |
||
4482 |
OP_EXIT, /* ( -- ) */ |
||
4483 |
OP_BCODE_HEADER, /* ( -- ) */ |
||
4484 |
OP_ARGS, /* ( -- ) Mark the beginning of function call arguments */ |
||
4485 |
OP_FOR_IN_NEXT, /* ( name obj iter_ptr -- name obj iter_ptr_next ) */ |
||
4486 |
OP_MAX |
||
4487 |
}; |
||
4488 |
|||
4489 |
struct pstate; |
||
4490 |
struct mjs; |
||
4491 |
|||
4492 |
MJS_PRIVATE void emit_byte(struct pstate *pstate, uint8_t byte); |
||
4493 |
MJS_PRIVATE void emit_int(struct pstate *pstate, int64_t n); |
||
4494 |
MJS_PRIVATE void emit_str(struct pstate *pstate, const char *ptr, size_t len); |
||
4495 |
|||
4496 |
/* |
||
4497 |
* Inserts provided offset `v` at the offset `offset`. |
||
4498 |
* |
||
4499 |
* Returns delta at which the code was moved; the delta can be any: 0 or |
||
4500 |
* positive or negative. |
||
4501 |
*/ |
||
4502 |
MJS_PRIVATE int mjs_bcode_insert_offset(struct pstate *p, struct mjs *mjs, |
||
4503 |
size_t offset, size_t v); |
||
4504 |
|||
4505 |
/* |
||
4506 |
* Adds a new bcode part; does not retain `bp`. |
||
4507 |
*/ |
||
4508 |
MJS_PRIVATE void mjs_bcode_part_add(struct mjs *mjs, |
||
4509 |
const struct mjs_bcode_part *bp); |
||
4510 |
|||
4511 |
/* |
||
4512 |
* Returns bcode part by the bcode number |
||
4513 |
*/ |
||
4514 |
MJS_PRIVATE struct mjs_bcode_part *mjs_bcode_part_get(struct mjs *mjs, int num); |
||
4515 |
|||
4516 |
/* |
||
4517 |
* Returns bcode part by the global bcode offset |
||
4518 |
*/ |
||
4519 |
MJS_PRIVATE struct mjs_bcode_part *mjs_bcode_part_get_by_offset(struct mjs *mjs, |
||
4520 |
size_t offset); |
||
4521 |
|||
4522 |
/* |
||
4523 |
* Returns a number of bcode parts |
||
4524 |
*/ |
||
4525 |
MJS_PRIVATE int mjs_bcode_parts_cnt(struct mjs *mjs); |
||
4526 |
|||
4527 |
/* |
||
4528 |
* Adds the bcode being generated (mjs->bcode_gen) as a next bcode part |
||
4529 |
*/ |
||
4530 |
MJS_PRIVATE void mjs_bcode_commit(struct mjs *mjs); |
||
4531 |
|||
4532 |
#if defined(__cplusplus) |
||
4533 |
} |
||
4534 |
#endif /* __cplusplus */ |
||
4535 |
|||
4536 |
#endif /* MJS_BCODE_H_ */ |
||
4537 |
#ifdef MJS_MODULE_LINES |
||
4538 |
#line 1 "mjs/src/mjs_internal.h" |
||
4539 |
#endif |
||
4540 |
/* |
||
4541 |
* Copyright (c) 2016 Cesanta Software Limited |
||
4542 |
* All rights reserved |
||
4543 |
*/ |
||
4544 |
|||
4545 |
#ifndef MJS_INTERNAL_H_ |
||
4546 |
#define MJS_INTERNAL_H_ |
||
4547 |
|||
4548 |
#include <assert.h> |
||
4549 |
#include <ctype.h> |
||
4550 |
#include <math.h> |
||
4551 |
#include <stdarg.h> |
||
4552 |
#include <stdio.h> |
||
4553 |
#include <string.h> |
||
4554 |
|||
4555 |
#ifndef FAST |
||
4556 |
#define FAST |
||
4557 |
#endif |
||
4558 |
|||
4559 |
#ifndef STATIC |
||
4560 |
#define STATIC |
||
4561 |
#endif |
||
4562 |
|||
4563 |
#ifndef ENDL |
||
4564 |
#define ENDL "\n" |
||
4565 |
#endif |
||
4566 |
|||
4567 |
#ifdef MJS_EXPOSE_PRIVATE |
||
4568 |
#define MJS_PRIVATE |
||
4569 |
#define MJS_EXTERN extern |
||
4570 |
#else |
||
4571 |
#define MJS_PRIVATE static |
||
4572 |
#define MJS_EXTERN static |
||
4573 |
#endif |
||
4574 |
|||
4575 |
#ifndef ARRAY_SIZE |
||
4576 |
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) |
||
4577 |
#endif |
||
4578 |
|||
4579 |
#if !defined(WEAK) |
||
4580 |
#if (defined(__GNUC__) || defined(__TI_COMPILER_VERSION__)) && !defined(_WIN32) |
||
4581 |
#define WEAK __attribute__((weak)) |
||
4582 |
#else |
||
4583 |
#define WEAK |
||
4584 |
#endif |
||
4585 |
#endif |
||
4586 |
|||
4587 |
#ifndef CS_ENABLE_STDIO |
||
4588 |
#define CS_ENABLE_STDIO 1 |
||
4589 |
#endif |
||
4590 |
|||
4591 |
/* Amalgamated: #include "common/cs_dbg.h" */ |
||
4592 |
/* Amalgamated: #include "common/cs_file.h" */ |
||
4593 |
/* Amalgamated: #include "common/mbuf.h" */ |
||
4594 |
|||
4595 |
#if defined(_WIN32) && _MSC_VER < 1700 |
||
4596 |
typedef signed char int8_t; |
||
4597 |
typedef unsigned char uint8_t; |
||
4598 |
typedef int int32_t; |
||
4599 |
typedef unsigned int uint32_t; |
||
4600 |
typedef short int16_t; |
||
4601 |
typedef unsigned short uint16_t; |
||
4602 |
typedef __int64 int64_t; |
||
4603 |
typedef unsigned long uintptr_t; |
||
4604 |
#define STRX(x) #x |
||
4605 |
#define STR(x) STRX(x) |
||
4606 |
#define __func__ __FILE__ ":" STR(__LINE__) |
||
4607 |
// #define snprintf _snprintf |
||
4608 |
#define vsnprintf _vsnprintf |
||
4609 |
#define isnan(x) _isnan(x) |
||
4610 |
#define va_copy(x, y) (x) = (y) |
||
4611 |
#define CS_DEFINE_DIRENT |
||
4612 |
#include <windows.h> |
||
4613 |
#else |
||
4614 |
#if defined(__unix__) || defined(__APPLE__) |
||
4615 |
#include <dlfcn.h> |
||
4616 |
#endif |
||
4617 |
#endif |
||
4618 |
|||
4619 |
/* |
||
4620 |
* Number of bytes reserved for the jump offset initially. The most practical |
||
4621 |
* value is 1, but for testing it's useful to set it to 0 and to some large |
||
4622 |
* value as well (like, 4), to make sure that the code behaves correctly under |
||
4623 |
* all circumstances. |
||
4624 |
*/ |
||
4625 |
#ifndef MJS_INIT_OFFSET_SIZE |
||
4626 |
#define MJS_INIT_OFFSET_SIZE 1 |
||
4627 |
#endif |
||
4628 |
|||
4629 |
#endif /* MJS_INTERNAL_H_ */ |
||
4630 |
#ifdef MJS_MODULE_LINES |
||
4631 |
#line 1 "mjs/src/mjs_tok.h" |
||
4632 |
#endif |
||
4633 |
/* |
||
4634 |
* Copyright (c) 2016 Cesanta Software Limited |
||
4635 |
* All rights reserved |
||
4636 |
*/ |
||
4637 |
|||
4638 |
#ifndef MJS_TOK_H_ |
||
4639 |
#define MJS_TOK_H_ |
||
4640 |
|||
4641 |
/* Amalgamated: #include "mjs_internal.h" */ |
||
4642 |
|||
4643 |
#if defined(__cplusplus) |
||
4644 |
extern "C" { |
||
4645 |
#endif /* __cplusplus */ |
||
4646 |
|||
4647 |
struct tok { |
||
4648 |
int tok; |
||
4649 |
int len; |
||
4650 |
const char *ptr; |
||
4651 |
}; |
||
4652 |
|||
4653 |
struct pstate { |
||
4654 |
const char *file_name; /* Source code file name */ |
||
4655 |
const char *buf; /* Nul-terminated source code buffer */ |
||
4656 |
const char *pos; /* Current position */ |
||
4657 |
int line_no; /* Line number */ |
||
4658 |
int last_emitted_line_no; |
||
4659 |
struct mbuf offset_lineno_map; |
||
4660 |
int prev_tok; /* Previous token, for prefix increment / decrement */ |
||
4661 |
struct tok tok; /* Parsed token */ |
||
4662 |
struct mjs *mjs; |
||
4663 |
int start_bcode_idx; /* Index in mjs->bcode at which parsing was started */ |
||
4664 |
int cur_idx; /* Index in mjs->bcode at which newly generated code is inserted |
||
4665 |
*/ |
||
4666 |
int depth; |
||
4667 |
}; |
||
4668 |
|||
4669 |
enum { |
||
4670 |
TOK_EOF, |
||
4671 |
TOK_INVALID, |
||
4672 |
|||
4673 |
TOK_COLON, |
||
4674 |
TOK_SEMICOLON, |
||
4675 |
TOK_COMMA, |
||
4676 |
TOK_ASSIGN, |
||
4677 |
TOK_OPEN_CURLY, |
||
4678 |
TOK_CLOSE_CURLY, |
||
4679 |
TOK_OPEN_PAREN, |
||
4680 |
TOK_CLOSE_PAREN, |
||
4681 |
TOK_OPEN_BRACKET, |
||
4682 |
TOK_CLOSE_BRACKET, |
||
4683 |
TOK_MUL, |
||
4684 |
TOK_PLUS, |
||
4685 |
TOK_MINUS, |
||
4686 |
TOK_DIV, |
||
4687 |
TOK_REM, |
||
4688 |
TOK_AND, |
||
4689 |
TOK_OR, |
||
4690 |
TOK_XOR, |
||
4691 |
TOK_DOT, |
||
4692 |
TOK_QUESTION, |
||
4693 |
TOK_NOT, |
||
4694 |
TOK_TILDA, |
||
4695 |
TOK_LT, |
||
4696 |
TOK_GT, |
||
4697 |
TOK_LSHIFT, |
||
4698 |
TOK_RSHIFT, |
||
4699 |
TOK_MINUS_MINUS, |
||
4700 |
TOK_PLUS_PLUS, |
||
4701 |
TOK_PLUS_ASSIGN, |
||
4702 |
TOK_MINUS_ASSIGN, |
||
4703 |
TOK_MUL_ASSIGN, |
||
4704 |
TOK_DIV_ASSIGN, |
||
4705 |
TOK_AND_ASSIGN, |
||
4706 |
TOK_OR_ASSIGN, |
||
4707 |
TOK_REM_ASSIGN, |
||
4708 |
TOK_XOR_ASSIGN, |
||
4709 |
TOK_EQ, |
||
4710 |
TOK_NE, |
||
4711 |
TOK_LE, |
||
4712 |
TOK_GE, |
||
4713 |
TOK_LOGICAL_AND, |
||
4714 |
TOK_LOGICAL_OR, |
||
4715 |
TOK_EQ_EQ, |
||
4716 |
TOK_NE_NE, |
||
4717 |
TOK_LSHIFT_ASSIGN, |
||
4718 |
TOK_RSHIFT_ASSIGN, |
||
4719 |
TOK_URSHIFT, |
||
4720 |
TOK_URSHIFT_ASSIGN, |
||
4721 |
|||
4722 |
TOK_UNARY_PLUS, |
||
4723 |
TOK_UNARY_MINUS, |
||
4724 |
TOK_POSTFIX_PLUS, |
||
4725 |
TOK_POSTFIX_MINUS, |
||
4726 |
|||
4727 |
TOK_NUM = 200, /* Make sure they don't clash with ascii '+', '{', etc */ |
||
4728 |
TOK_STR, |
||
4729 |
TOK_IDENT, |
||
4730 |
TOK_KEYWORD_BREAK, |
||
4731 |
TOK_KEYWORD_CASE, |
||
4732 |
TOK_KEYWORD_CATCH, |
||
4733 |
TOK_KEYWORD_CONTINUE, |
||
4734 |
TOK_KEYWORD_DEBUGGER, |
||
4735 |
TOK_KEYWORD_DEFAULT, |
||
4736 |
TOK_KEYWORD_DELETE, |
||
4737 |
TOK_KEYWORD_DO, |
||
4738 |
TOK_KEYWORD_ELSE, |
||
4739 |
TOK_KEYWORD_FALSE, |
||
4740 |
TOK_KEYWORD_FINALLY, |
||
4741 |
TOK_KEYWORD_FOR, |
||
4742 |
TOK_KEYWORD_FUNCTION, |
||
4743 |
TOK_KEYWORD_IF, |
||
4744 |
TOK_KEYWORD_IN, |
||
4745 |
TOK_KEYWORD_INSTANCEOF, |
||
4746 |
TOK_KEYWORD_NEW, |
||
4747 |
TOK_KEYWORD_NULL, |
||
4748 |
TOK_KEYWORD_RETURN, |
||
4749 |
TOK_KEYWORD_SWITCH, |
||
4750 |
TOK_KEYWORD_THIS, |
||
4751 |
TOK_KEYWORD_THROW, |
||
4752 |
TOK_KEYWORD_TRUE, |
||
4753 |
TOK_KEYWORD_TRY, |
||
4754 |
TOK_KEYWORD_TYPEOF, |
||
4755 |
TOK_KEYWORD_VAR, |
||
4756 |
TOK_KEYWORD_VOID, |
||
4757 |
TOK_KEYWORD_WHILE, |
||
4758 |
TOK_KEYWORD_WITH, |
||
4759 |
TOK_KEYWORD_LET, |
||
4760 |
TOK_KEYWORD_UNDEFINED, |
||
4761 |
TOK_MAX |
||
4762 |
}; |
||
4763 |
|||
4764 |
MJS_PRIVATE void pinit(const char *file_name, const char *buf, struct pstate *); |
||
4765 |
MJS_PRIVATE int pnext(struct pstate *); |
||
4766 |
MJS_PRIVATE int mjs_is_ident(int c); |
||
4767 |
MJS_PRIVATE int mjs_is_digit(int c); |
||
4768 |
|||
4769 |
#if defined(__cplusplus) |
||
4770 |
} |
||
4771 |
#endif /* __cplusplus */ |
||
4772 |
|||
4773 |
#endif /* MJS_TOK_H_ */ |
||
4774 |
#ifdef MJS_MODULE_LINES |
||
4775 |
#line 1 "mjs/src/mjs_dataview.h" |
||
4776 |
#endif |
||
4777 |
/* |
||
4778 |
* Copyright (c) 2017 Cesanta Software Limited |
||
4779 |
* All rights reserved |
||
4780 |
*/ |
||
4781 |
|||
4782 |
#ifndef MJS_DATAVIEW_H_ |
||
4783 |
#define MJS_DATAVIEW_H_ |
||
4784 |
|||
4785 |
#if defined(__cplusplus) |
||
4786 |
extern "C" { |
||
4787 |
#endif /* __cplusplus */ |
||
4788 |
|||
4789 |
/* |
||
4790 |
* Functions for memory introspection. |
||
4791 |
* These are supposed to be FFI-ed and used from the JS environment. |
||
4792 |
*/ |
||
4793 |
|||
4794 |
void *mjs_mem_to_ptr(unsigned int val); |
||
4795 |
void *mjs_mem_get_ptr(void *base, int offset); |
||
4796 |
void mjs_mem_set_ptr(void *ptr, void *val); |
||
4797 |
double mjs_mem_get_dbl(void *ptr); |
||
4798 |
void mjs_mem_set_dbl(void *ptr, double val); |
||
4799 |
double mjs_mem_get_uint(void *ptr, int size, int bigendian); |
||
4800 |
double mjs_mem_get_int(void *ptr, int size, int bigendian); |
||
4801 |
void mjs_mem_set_uint(void *ptr, unsigned int val, int size, int bigendian); |
||
4802 |
void mjs_mem_set_int(void *ptr, int val, int size, int bigendian); |
||
4803 |
|||
4804 |
#if defined(__cplusplus) |
||
4805 |
} |
||
4806 |
#endif /* __cplusplus */ |
||
4807 |
|||
4808 |
#endif /* MJS_DATAVIEW_H_ */ |
||
4809 |
#ifdef MJS_MODULE_LINES |
||
4810 |
#line 1 "mjs/src/mjs_exec_public.h" |
||
4811 |
#endif |
||
4812 |
/* |
||
4813 |
* Copyright (c) 2016 Cesanta Software Limited |
||
4814 |
* All rights reserved |
||
4815 |
*/ |
||
4816 |
|||
4817 |
#ifndef MJS_EXEC_PUBLIC_H_ |
||
4818 |
#define MJS_EXEC_PUBLIC_H_ |
||
4819 |
|||
4820 |
/* Amalgamated: #include "mjs/src/mjs_core_public.h" */ |
||
4821 |
#include <stdio.h> |
||
4822 |
|||
4823 |
#if defined(__cplusplus) |
||
4824 |
extern "C" { |
||
4825 |
#endif /* __cplusplus */ |
||
4826 |
|||
4827 |
mjs_err_t mjs_exec(struct mjs *, const char *src, mjs_val_t *res); |
||
4828 |
mjs_err_t mjs_exec_buf(struct mjs *, const char *src, size_t, mjs_val_t *res); |
||
4829 |
|||
4830 |
mjs_err_t mjs_exec_file(struct mjs *mjs, const char *path, mjs_val_t *res); |
||
4831 |
mjs_err_t mjs_apply(struct mjs *mjs, mjs_val_t *res, mjs_val_t func, |
||
4832 |
mjs_val_t this_val, int nargs, mjs_val_t *args); |
||
4833 |
mjs_err_t mjs_call(struct mjs *mjs, mjs_val_t *res, mjs_val_t func, |
||
4834 |
mjs_val_t this_val, int nargs, ...); |
||
4835 |
mjs_val_t mjs_get_this(struct mjs *mjs); |
||
4836 |
|||
4837 |
#if defined(__cplusplus) |
||
4838 |
} |
||
4839 |
#endif /* __cplusplus */ |
||
4840 |
|||
4841 |
#endif /* MJS_EXEC_PUBLIC_H_ */ |
||
4842 |
#ifdef MJS_MODULE_LINES |
||
4843 |
#line 1 "mjs/src/mjs_exec.h" |
||
4844 |
#endif |
||
4845 |
/* |
||
4846 |
* Copyright (c) 2016 Cesanta Software Limited |
||
4847 |
* All rights reserved |
||
4848 |
*/ |
||
4849 |
|||
4850 |
#ifndef MJS_EXEC_H_ |
||
4851 |
#define MJS_EXEC_H_ |
||
4852 |
|||
4853 |
/* Amalgamated: #include "mjs/src/mjs_exec_public.h" */ |
||
4854 |
|||
4855 |
/* |
||
4856 |
* A special bcode offset value which causes mjs_execute() to exit immediately; |
||
4857 |
* used in mjs_apply(). |
||
4858 |
*/ |
||
4859 |
#define MJS_BCODE_OFFSET_EXIT ((size_t) 0x7fffffff) |
||
4860 |
|||
4861 |
#if defined(__cplusplus) |
||
4862 |
extern "C" { |
||
4863 |
#endif /* __cplusplus */ |
||
4864 |
|||
4865 |
MJS_PRIVATE mjs_err_t mjs_execute(struct mjs *mjs, size_t off, mjs_val_t *res); |
||
4866 |
|||
4867 |
#if defined(__cplusplus) |
||
4868 |
} |
||
4869 |
#endif /* __cplusplus */ |
||
4870 |
|||
4871 |
#endif /* MJS_EXEC_H_ */ |
||
4872 |
#ifdef MJS_MODULE_LINES |
||
4873 |
#line 1 "mjs/src/mjs_json.h" |
||
4874 |
#endif |
||
4875 |
/* |
||
4876 |
* Copyright (c) 2016 Cesanta Software Limited |
||
4877 |
* All rights reserved |
||
4878 |
*/ |
||
4879 |
|||
4880 |
#ifndef MJS_JSON_H_ |
||
4881 |
#define MJS_JSON_H_ |
||
4882 |
|||
4883 |
#if defined(__cplusplus) |
||
4884 |
extern "C" { |
||
4885 |
#endif /* __cplusplus */ |
||
4886 |
|||
4887 |
MJS_PRIVATE mjs_err_t to_json_or_debug(struct mjs *mjs, mjs_val_t v, char *buf, |
||
4888 |
size_t size, size_t *res_len, |
||
4889 |
uint8_t is_debug); |
||
4890 |
|||
4891 |
MJS_PRIVATE mjs_err_t mjs_json_stringify(struct mjs *mjs, mjs_val_t v, |
||
4892 |
char *buf, size_t size, char **res); |
||
4893 |
MJS_PRIVATE void mjs_op_json_stringify(struct mjs *mjs); |
||
4894 |
MJS_PRIVATE void mjs_op_json_parse(struct mjs *mjs); |
||
4895 |
|||
4896 |
MJS_PRIVATE mjs_err_t |
||
4897 |
mjs_json_parse(struct mjs *mjs, const char *str, size_t len, mjs_val_t *res); |
||
4898 |
|||
4899 |
#if defined(__cplusplus) |
||
4900 |
} |
||
4901 |
#endif /* __cplusplus */ |
||
4902 |
|||
4903 |
#endif /* MJS_JSON_H_ */ |
||
4904 |
#ifdef MJS_MODULE_LINES |
||
4905 |
#line 1 "mjs/src/mjs_builtin.h" |
||
4906 |
#endif |
||
4907 |
/* |
||
4908 |
* Copyright (c) 2016 Cesanta Software Limited |
||
4909 |
* All rights reserved |
||
4910 |
*/ |
||
4911 |
|||
4912 |
#ifndef MJS_BUILTIN_H_ |
||
4913 |
#define MJS_BUILTIN_H_ |
||
4914 |
|||
4915 |
/* Amalgamated: #include "mjs/src/mjs_core_public.h" */ |
||
4916 |
/* Amalgamated: #include "mjs/src/mjs_internal.h" */ |
||
4917 |
|||
4918 |
#if defined(__cplusplus) |
||
4919 |
extern "C" { |
||
4920 |
#endif /* __cplusplus */ |
||
4921 |
|||
4922 |
void mjs_init_builtin(struct mjs *mjs, mjs_val_t obj); |
||
4923 |
|||
4924 |
#if defined(__cplusplus) |
||
4925 |
} |
||
4926 |
#endif /* __cplusplus */ |
||
4927 |
|||
4928 |
#endif /* MJS_BUILTIN_H_ */ |
||
4929 |
#ifdef MJS_MODULE_LINES |
||
4930 |
#line 1 "mjs/src/mjs_parser.h" |
||
4931 |
#endif |
||
4932 |
/* |
||
4933 |
* Copyright (c) 2016 Cesanta Software Limited |
||
4934 |
* All rights reserved |
||
4935 |
*/ |
||
4936 |
|||
4937 |
#ifndef MJS_PARSER_H |
||
4938 |
#define MJS_PARSER_H |
||
4939 |
|||
4940 |
/* Amalgamated: #include "mjs/src/mjs_internal.h" */ |
||
4941 |
|||
4942 |
#if defined(__cplusplus) |
||
4943 |
extern "C" { |
||
4944 |
#endif /* __cplusplus */ |
||
4945 |
|||
4946 |
MJS_PRIVATE mjs_err_t |
||
4947 |
mjs_parse(const char *path, const char *buf, struct mjs *); |
||
4948 |
|||
4949 |
#if defined(__cplusplus) |
||
4950 |
} |
||
4951 |
#endif /* __cplusplus */ |
||
4952 |
|||
4953 |
#endif /* MJS_PARSER_H */ |
||
4954 |
#ifndef MJS_EXPORT_INTERNAL_HEADERS |
||
4955 |
#ifdef MJS_MODULE_LINES |
||
4956 |
#line 1 "common/cs_dbg.c" |
||
4957 |
#endif |
||
4958 |
/* |
||
4959 |
* Copyright (c) 2014-2018 Cesanta Software Limited |
||
4960 |
* All rights reserved |
||
4961 |
* |
||
4962 |
* Licensed under the Apache License, Version 2.0 (the ""License""); |
||
4963 |
* you may not use this file except in compliance with the License. |
||
4964 |
* You may obtain a copy of the License at |
||
4965 |
* |
||
4966 |
* http://www.apache.org/licenses/LICENSE-2.0 |
||
4967 |
* |
||
4968 |
* Unless required by applicable law or agreed to in writing, software |
||
4969 |
* distributed under the License is distributed on an ""AS IS"" BASIS, |
||
4970 |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||
4971 |
* See the License for the specific language governing permissions and |
||
4972 |
* limitations under the License. |
||
4973 |
*/ |
||
4974 |
|||
4975 |
/* Amalgamated: #include "common/cs_dbg.h" */ |
||
4976 |
|||
4977 |
#include <stdarg.h> |
||
4978 |
#include <stdio.h> |
||
4979 |
#include <string.h> |
||
4980 |
|||
4981 |
/* Amalgamated: #include "common/cs_time.h" */ |
||
4982 |
/* Amalgamated: #include "common/str_util.h" */ |
||
4983 |
|||
4984 |
enum cs_log_level cs_log_threshold WEAK = |
||
4985 |
#if CS_ENABLE_DEBUG |
||
4986 |
LL_VERBOSE_DEBUG; |
||
4987 |
#else |
||
4988 |
LL_ERROR; |
||
4989 |
#endif |
||
4990 |
|||
4991 |
static char *s_filter_pattern = NULL; |
||
4992 |
static size_t s_filter_pattern_len; |
||
4993 |
|||
4994 |
void cs_log_set_filter(const char *pattern) WEAK; |
||
4995 |
|||
4996 |
#if CS_ENABLE_STDIO |
||
4997 |
|||
4998 |
FILE *cs_log_file WEAK = NULL; |
||
4999 |
|||
5000 |
#if CS_LOG_ENABLE_TS_DIFF |
||
5001 |
double cs_log_ts WEAK; |
||
5002 |
#endif |
||
5003 |
|||
5004 |
enum cs_log_level cs_log_cur_msg_level WEAK = LL_NONE; |
||
5005 |
|||
5006 |
void cs_log_set_filter(const char *pattern) { |
||
5007 |
free(s_filter_pattern); |
||
5008 |
if (pattern != NULL) { |
||
5009 |
s_filter_pattern = strdup(pattern); |
||
5010 |
s_filter_pattern_len = strlen(pattern); |
||
5011 |
} else { |
||
5012 |
s_filter_pattern = NULL; |
||
5013 |
s_filter_pattern_len = 0; |
||
5014 |
} |
||
5015 |
} |
||
5016 |
|||
5017 |
int cs_log_print_prefix(enum cs_log_level, const char *, const char *) WEAK; |
||
5018 |
1297 |
int cs_log_print_prefix(enum cs_log_level level, const char *func, |
|
5019 |
const char *filename) { |
||
5020 |
char prefix[21]; |
||
5021 |
|||
5022 |
✓✗ | 1297 |
if (level > cs_log_threshold) return 0; |
5023 |
if (s_filter_pattern != NULL && |
||
5024 |
mg_match_prefix(s_filter_pattern, s_filter_pattern_len, func) == 0 && |
||
5025 |
mg_match_prefix(s_filter_pattern, s_filter_pattern_len, filename) == 0) { |
||
5026 |
return 0; |
||
5027 |
} |
||
5028 |
|||
5029 |
strncpy(prefix, func, 20); |
||
5030 |
prefix[20] = '\0'; |
||
5031 |
if (cs_log_file == NULL) cs_log_file = stderr; |
||
5032 |
cs_log_cur_msg_level = level; |
||
5033 |
fprintf(cs_log_file, "%-20s ", prefix); |
||
5034 |
#if CS_LOG_ENABLE_TS_DIFF |
||
5035 |
{ |
||
5036 |
double now = cs_time(); |
||
5037 |
fprintf(cs_log_file, "%7u ", (unsigned int) ((now - cs_log_ts) * 1000000)); |
||
5038 |
cs_log_ts = now; |
||
5039 |
} |
||
5040 |
#endif |
||
5041 |
return 1; |
||
5042 |
} |
||
5043 |
|||
5044 |
void cs_log_printf(const char *fmt, ...) WEAK; |
||
5045 |
void cs_log_printf(const char *fmt, ...) { |
||
5046 |
va_list ap; |
||
5047 |
va_start(ap, fmt); |
||
5048 |
vfprintf(cs_log_file, fmt, ap); |
||
5049 |
va_end(ap); |
||
5050 |
fputc('\n', cs_log_file); |
||
5051 |
fflush(cs_log_file); |
||
5052 |
cs_log_cur_msg_level = LL_NONE; |
||
5053 |
} |
||
5054 |
|||
5055 |
void cs_log_set_file(FILE *file) WEAK; |
||
5056 |
void cs_log_set_file(FILE *file) { |
||
5057 |
cs_log_file = file; |
||
5058 |
} |
||
5059 |
|||
5060 |
#else |
||
5061 |
|||
5062 |
void cs_log_set_filter(const char *pattern) { |
||
5063 |
(void) pattern; |
||
5064 |
} |
||
5065 |
|||
5066 |
#endif /* CS_ENABLE_STDIO */ |
||
5067 |
|||
5068 |
void cs_log_set_level(enum cs_log_level level) WEAK; |
||
5069 |
void cs_log_set_level(enum cs_log_level level) { |
||
5070 |
cs_log_threshold = level; |
||
5071 |
#if CS_LOG_ENABLE_TS_DIFF && CS_ENABLE_STDIO |
||
5072 |
cs_log_ts = cs_time(); |
||
5073 |
#endif |
||
5074 |
} |
||
5075 |
#ifdef MJS_MODULE_LINES |
||
5076 |
#line 1 "common/cs_file.c" |
||
5077 |
#endif |
||
5078 |
/* |
||
5079 |
* Copyright (c) 2014-2018 Cesanta Software Limited |
||
5080 |
* All rights reserved |
||
5081 |
* |
||
5082 |
* Licensed under the Apache License, Version 2.0 (the ""License""); |
||
5083 |
* you may not use this file except in compliance with the License. |
||
5084 |
* You may obtain a copy of the License at |
||
5085 |
* |
||
5086 |
* http://www.apache.org/licenses/LICENSE-2.0 |
||
5087 |
* |
||
5088 |
* Unless required by applicable law or agreed to in writing, software |
||
5089 |
* distributed under the License is distributed on an ""AS IS"" BASIS, |
||
5090 |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||
5091 |
* See the License for the specific language governing permissions and |
||
5092 |
* limitations under the License. |
||
5093 |
*/ |
||
5094 |
|||
5095 |
/* Amalgamated: #include "common/cs_file.h" */ |
||
5096 |
|||
5097 |
#include <stdio.h> |
||
5098 |
#include <stdlib.h> |
||
5099 |
|||
5100 |
#ifdef CS_MMAP |
||
5101 |
#include <fcntl.h> |
||
5102 |
#include <sys/mman.h> |
||
5103 |
#include <sys/stat.h> |
||
5104 |
#endif |
||
5105 |
|||
5106 |
#ifndef EXCLUDE_COMMON |
||
5107 |
char *cs_read_file(const char *path, size_t *size) WEAK; |
||
5108 |
char *cs_read_file(const char *path, size_t *size) { |
||
5109 |
FILE *fp; |
||
5110 |
char *data = NULL; |
||
5111 |
if ((fp = fopen(path, "rb")) == NULL) { |
||
5112 |
} else if (fseek(fp, 0, SEEK_END) != 0) { |
||
5113 |
fclose(fp); |
||
5114 |
} else { |
||
5115 |
*size = ftell(fp); |
||
5116 |
data = (char *) malloc(*size + 1); |
||
5117 |
if (data != NULL) { |
||
5118 |
fseek(fp, 0, SEEK_SET); /* Some platforms might not have rewind(), Oo */ |
||
5119 |
if (fread(data, 1, *size, fp) != *size) { |
||
5120 |
free(data); |
||
5121 |
return NULL; |
||
5122 |
} |
||
5123 |
data[*size] = '\0'; |
||
5124 |
} |
||
5125 |
fclose(fp); |
||
5126 |
} |
||
5127 |
return data; |
||
5128 |
} |
||
5129 |
#endif /* EXCLUDE_COMMON */ |
||
5130 |
|||
5131 |
#ifdef CS_MMAP |
||
5132 |
char *cs_mmap_file(const char *path, size_t *size) WEAK; |
||
5133 |
char *cs_mmap_file(const char *path, size_t *size) { |
||
5134 |
char *r; |
||
5135 |
int fd = open(path, O_RDONLY, 0); |
||
5136 |
struct stat st; |
||
5137 |
if (fd < 0) return NULL; |
||
5138 |
fstat(fd, &st); |
||
5139 |
*size = (size_t) st.st_size; |
||
5140 |
r = (char *) mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); |
||
5141 |
if (r == MAP_FAILED) return NULL; |
||
5142 |
return r; |
||
5143 |
} |
||
5144 |
#endif |
||
5145 |
#ifdef MJS_MODULE_LINES |
||
5146 |
#line 1 "common/cs_varint.c" |
||
5147 |
#endif |
||
5148 |
/* |
||
5149 |
* Copyright (c) 2014-2018 Cesanta Software Limited |
||
5150 |
* All rights reserved |
||
5151 |
* |
||
5152 |
* Licensed under the Apache License, Version 2.0 (the ""License""); |
||
5153 |
* you may not use this file except in compliance with the License. |
||
5154 |
* You may obtain a copy of the License at |
||
5155 |
* |
||
5156 |
* http://www.apache.org/licenses/LICENSE-2.0 |
||
5157 |
* |
||
5158 |
* Unless required by applicable law or agreed to in writing, software |
||
5159 |
* distributed under the License is distributed on an ""AS IS"" BASIS, |
||
5160 |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||
5161 |
* See the License for the specific language governing permissions and |
||
5162 |
* limitations under the License. |
||
5163 |
*/ |
||
5164 |
|||
5165 |
/* Amalgamated: #include "cs_varint.h" */ |
||
5166 |
|||
5167 |
720 |
size_t cs_varint_llen(uint64_t num) { |
|
5168 |
720 |
size_t llen = 0; |
|
5169 |
|||
5170 |
do { |
||
5171 |
724 |
llen++; |
|
5172 |
✓✓ | 724 |
} while (num >>= 7); |
5173 |
|||
5174 |
720 |
return llen; |
|
5175 |
} |
||
5176 |
|||
5177 |
720 |
size_t cs_varint_encode(uint64_t num, uint8_t *buf, size_t buf_size) { |
|
5178 |
720 |
size_t llen = 0; |
|
5179 |
|||
5180 |
do { |
||
5181 |
724 |
uint8_t byte = num & 0x7f; |
|
5182 |
724 |
num >>= 7; |
|
5183 |
✓✓ | 724 |
if (num != 0) byte |= 0x80; |
5184 |
✓✗ | 724 |
if (llen < buf_size) *buf++ = byte; |
5185 |
724 |
llen++; |
|
5186 |
✓✓ | 724 |
} while (num != 0); |
5187 |
|||
5188 |
720 |
return llen; |
|
5189 |
} |
||
5190 |
|||
5191 |
3375 |
bool cs_varint_decode(const uint8_t *buf, size_t buf_size, uint64_t *num, |
|
5192 |
size_t *llen) { |
||
5193 |
3375 |
size_t i = 0, shift = 0; |
|
5194 |
3375 |
uint64_t n = 0; |
|
5195 |
|||
5196 |
do { |
||
5197 |
✓✗✗✓ |
3381 |
if (i == buf_size || i == (8 * sizeof(*num) / 7 + 1)) return false; |
5198 |
/* |
||
5199 |
* Each byte of varint contains 7 bits, in little endian order. |
||
5200 |
* MSB is a continuation bit: it tells whether next byte is used. |
||
5201 |
*/ |
||
5202 |
3381 |
n |= ((uint64_t)(buf[i] & 0x7f)) << shift; |
|
5203 |
/* |
||
5204 |
* First we increment i, then check whether it is within boundary and |
||
5205 |
* whether decoded byte had continuation bit set. |
||
5206 |
*/ |
||
5207 |
3381 |
i++; |
|
5208 |
3381 |
shift += 7; |
|
5209 |
✓✗✓✓ |
3381 |
} while (shift < sizeof(uint64_t) * 8 && (buf[i - 1] & 0x80)); |
5210 |
|||
5211 |
3375 |
*num = n; |
|
5212 |
3375 |
*llen = i; |
|
5213 |
|||
5214 |
3375 |
return true; |
|
5215 |
} |
||
5216 |
|||
5217 |
483 |
uint64_t cs_varint_decode_unsafe(const uint8_t *buf, int *llen) { |
|
5218 |
uint64_t v; |
||
5219 |
size_t l; |
||
5220 |
483 |
cs_varint_decode(buf, ~0, &v, &l); |
|
5221 |
483 |
*llen = l; |
|
5222 |
483 |
return v; |
|
5223 |
} |
||
5224 |
#ifdef MJS_MODULE_LINES |
||
5225 |
#line 1 "common/mbuf.c" |
||
5226 |
#endif |
||
5227 |
/* |
||
5228 |
* Copyright (c) 2014-2018 Cesanta Software Limited |
||
5229 |
* All rights reserved |
||
5230 |
* |
||
5231 |
* Licensed under the Apache License, Version 2.0 (the ""License""); |
||
5232 |
* you may not use this file except in compliance with the License. |
||
5233 |
* You may obtain a copy of the License at |
||
5234 |
* |
||
5235 |
* http://www.apache.org/licenses/LICENSE-2.0 |
||
5236 |
* |
||
5237 |
* Unless required by applicable law or agreed to in writing, software |
||
5238 |
* distributed under the License is distributed on an ""AS IS"" BASIS, |
||
5239 |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||
5240 |
* See the License for the specific language governing permissions and |
||
5241 |
* limitations under the License. |
||
5242 |
*/ |
||
5243 |
|||
5244 |
#ifndef EXCLUDE_COMMON |
||
5245 |
|||
5246 |
#include <assert.h> |
||
5247 |
#include <string.h> |
||
5248 |
/* Amalgamated: #include "common/mbuf.h" */ |
||
5249 |
|||
5250 |
#ifndef MBUF_REALLOC |
||
5251 |
#define MBUF_REALLOC realloc |
||
5252 |
#endif |
||
5253 |
|||
5254 |
#ifndef MBUF_FREE |
||
5255 |
#define MBUF_FREE free |
||
5256 |
#endif |
||
5257 |
|||
5258 |
void mbuf_init(struct mbuf *mbuf, size_t initial_size) WEAK; |
||
5259 |
1310 |
void mbuf_init(struct mbuf *mbuf, size_t initial_size) { |
|
5260 |
1310 |
mbuf->len = mbuf->size = 0; |
|
5261 |
1310 |
mbuf->buf = NULL; |
|
5262 |
1310 |
mbuf_resize(mbuf, initial_size); |
|
5263 |
1310 |
} |
|
5264 |
|||
5265 |
void mbuf_free(struct mbuf *mbuf) WEAK; |
||
5266 |
948 |
void mbuf_free(struct mbuf *mbuf) { |
|
5267 |
✓✓ | 948 |
if (mbuf->buf != NULL) { |
5268 |
308 |
MBUF_FREE(mbuf->buf); |
|
5269 |
308 |
mbuf_init(mbuf, 0); |
|
5270 |
} |
||
5271 |
948 |
} |
|
5272 |
|||
5273 |
void mbuf_resize(struct mbuf *a, size_t new_size) WEAK; |
||
5274 |
1541 |
void mbuf_resize(struct mbuf *a, size_t new_size) { |
|
5275 |
✓✓✓✓ ✓✗ |
1541 |
if (new_size > a->size || (new_size < a->size && new_size >= a->len)) { |
5276 |
227 |
char *buf = (char *) MBUF_REALLOC(a->buf, new_size); |
|
5277 |
/* |
||
5278 |
* In case realloc fails, there's not much we can do, except keep things as |
||
5279 |
* they are. Note that NULL is a valid return value from realloc when |
||
5280 |
* size == 0, but that is covered too. |
||
5281 |
*/ |
||
5282 |
✗✓✗✗ |
227 |
if (buf == NULL && new_size != 0) return; |
5283 |
227 |
a->buf = buf; |
|
5284 |
227 |
a->size = new_size; |
|
5285 |
} |
||
5286 |
} |
||
5287 |
|||
5288 |
void mbuf_trim(struct mbuf *mbuf) WEAK; |
||
5289 |
66 |
void mbuf_trim(struct mbuf *mbuf) { |
|
5290 |
66 |
mbuf_resize(mbuf, mbuf->len); |
|
5291 |
66 |
} |
|
5292 |
|||
5293 |
size_t mbuf_insert(struct mbuf *a, size_t off, const void *buf, size_t) WEAK; |
||
5294 |
1928 |
size_t mbuf_insert(struct mbuf *a, size_t off, const void *buf, size_t len) { |
|
5295 |
1928 |
char *p = NULL; |
|
5296 |
|||
5297 |
✗✓ | 1928 |
assert(a != NULL); |
5298 |
✗✓ | 1928 |
assert(a->len <= a->size); |
5299 |
✗✓ | 1928 |
assert(off <= a->len); |
5300 |
|||
5301 |
/* check overflow */ |
||
5302 |
✗✓ | 1928 |
if (~(size_t) 0 - (size_t) a->buf < len) return 0; |
5303 |
|||
5304 |
✓✓ | 1928 |
if (a->len + len <= a->size) { |
5305 |
1315 |
memmove(a->buf + off + len, a->buf + off, a->len - off); |
|
5306 |
✓✓ | 1315 |
if (buf != NULL) { |
5307 |
645 |
memcpy(a->buf + off, buf, len); |
|
5308 |
} |
||
5309 |
1315 |
a->len += len; |
|
5310 |
} else { |
||
5311 |
613 |
size_t min_size = (a->len + len); |
|
5312 |
613 |
size_t new_size = (size_t)(min_size * MBUF_SIZE_MULTIPLIER); |
|
5313 |
✗✓ | 613 |
if (new_size - min_size > MBUF_SIZE_MAX_HEADROOM) { |
5314 |
new_size = min_size + MBUF_SIZE_MAX_HEADROOM; |
||
5315 |
} |
||
5316 |
613 |
p = (char *) MBUF_REALLOC(a->buf, new_size); |
|
5317 |
✗✓✗✗ |
613 |
if (p == NULL && new_size != min_size) { |
5318 |
new_size = min_size; |
||
5319 |
p = (char *) MBUF_REALLOC(a->buf, new_size); |
||
5320 |
} |
||
5321 |
✓✗ | 613 |
if (p != NULL) { |
5322 |
613 |
a->buf = p; |
|
5323 |
✗✓ | 613 |
if (off != a->len) { |
5324 |
memmove(a->buf + off + len, a->buf + off, a->len - off); |
||
5325 |
} |
||
5326 |
✓✓ | 613 |
if (buf != NULL) memcpy(a->buf + off, buf, len); |
5327 |
613 |
a->len += len; |
|
5328 |
613 |
a->size = new_size; |
|
5329 |
} else { |
||
5330 |
len = 0; |
||
5331 |
} |
||
5332 |
} |
||
5333 |
|||
5334 |
1928 |
return len; |
|
5335 |
} |
||
5336 |
|||
5337 |
size_t mbuf_append(struct mbuf *a, const void *buf, size_t len) WEAK; |
||
5338 |
651 |
size_t mbuf_append(struct mbuf *a, const void *buf, size_t len) { |
|
5339 |
651 |
return mbuf_insert(a, a->len, buf, len); |
|
5340 |
} |
||
5341 |
|||
5342 |
void mbuf_remove(struct mbuf *mb, size_t n) WEAK; |
||
5343 |
void mbuf_remove(struct mbuf *mb, size_t n) { |
||
5344 |
if (n > 0 && n <= mb->len) { |
||
5345 |
memmove(mb->buf, mb->buf + n, mb->len - n); |
||
5346 |
mb->len -= n; |
||
5347 |
} |
||
5348 |
} |
||
5349 |
|||
5350 |
#endif /* EXCLUDE_COMMON */ |
||
5351 |
#ifdef MJS_MODULE_LINES |
||
5352 |
#line 1 "common/mg_str.c" |
||
5353 |
#endif |
||
5354 |
/* |
||
5355 |
* Copyright (c) 2014-2018 Cesanta Software Limited |
||
5356 |
* All rights reserved |
||
5357 |
* |
||
5358 |
* Licensed under the Apache License, Version 2.0 (the ""License""); |
||
5359 |
* you may not use this file except in compliance with the License. |
||
5360 |
* You may obtain a copy of the License at |
||
5361 |
* |
||
5362 |
* http://www.apache.org/licenses/LICENSE-2.0 |
||
5363 |
* |
||
5364 |
* Unless required by applicable law or agreed to in writing, software |
||
5365 |
* distributed under the License is distributed on an ""AS IS"" BASIS, |
||
5366 |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||
5367 |
* See the License for the specific language governing permissions and |
||
5368 |
* limitations under the License. |
||
5369 |
*/ |
||
5370 |
|||
5371 |
/* Amalgamated: #include "common/mg_mem.h" */ |
||
5372 |
/* Amalgamated: #include "common/mg_str.h" */ |
||
5373 |
/* Amalgamated: #include "common/platform.h" */ |
||
5374 |
|||
5375 |
#include <stdlib.h> |
||
5376 |
#include <string.h> |
||
5377 |
|||
5378 |
int mg_ncasecmp(const char *s1, const char *s2, size_t len) WEAK; |
||
5379 |
|||
5380 |
struct mg_str mg_mk_str(const char *s) WEAK; |
||
5381 |
struct mg_str mg_mk_str(const char *s) { |
||
5382 |
struct mg_str ret = {s, 0}; |
||
5383 |
if (s != NULL) ret.len = strlen(s); |
||
5384 |
return ret; |
||
5385 |
} |
||
5386 |
|||
5387 |
struct mg_str mg_mk_str_n(const char *s, size_t len) WEAK; |
||
5388 |
struct mg_str mg_mk_str_n(const char *s, size_t len) { |
||
5389 |
struct mg_str ret = {s, len}; |
||
5390 |
return ret; |
||
5391 |
} |
||
5392 |
|||
5393 |
int mg_vcmp(const struct mg_str *str1, const char *str2) WEAK; |
||
5394 |
int mg_vcmp(const struct mg_str *str1, const char *str2) { |
||
5395 |
size_t n2 = strlen(str2), n1 = str1->len; |
||
5396 |
int r = strncmp(str1->p, str2, (n1 < n2) ? n1 : n2); |
||
5397 |
if (r == 0) { |
||
5398 |
return n1 - n2; |
||
5399 |
} |
||
5400 |
return r; |
||
5401 |
} |
||
5402 |
|||
5403 |
int mg_vcasecmp(const struct mg_str *str1, const char *str2) WEAK; |
||
5404 |
int mg_vcasecmp(const struct mg_str *str1, const char *str2) { |
||
5405 |
size_t n2 = strlen(str2), n1 = str1->len; |
||
5406 |
int r = mg_ncasecmp(str1->p, str2, (n1 < n2) ? n1 : n2); |
||
5407 |
if (r == 0) { |
||
5408 |
return n1 - n2; |
||
5409 |
} |
||
5410 |
return r; |
||
5411 |
} |
||
5412 |
|||
5413 |
static struct mg_str mg_strdup_common(const struct mg_str s, |
||
5414 |
int nul_terminate) { |
||
5415 |
struct mg_str r = {NULL, 0}; |
||
5416 |
if (s.len > 0 && s.p != NULL) { |
||
5417 |
char *sc = (char *) MG_MALLOC(s.len + (nul_terminate ? 1 : 0)); |
||
5418 |
if (sc != NULL) { |
||
5419 |
memcpy(sc, s.p, s.len); |
||
5420 |
if (nul_terminate) sc[s.len] = '\0'; |
||
5421 |
r.p = sc; |
||
5422 |
r.len = s.len; |
||
5423 |
} |
||
5424 |
} |
||
5425 |
return r; |
||
5426 |
} |
||
5427 |
|||
5428 |
struct mg_str mg_strdup(const struct mg_str s) WEAK; |
||
5429 |
struct mg_str mg_strdup(const struct mg_str s) { |
||
5430 |
return mg_strdup_common(s, 0 /* NUL-terminate */); |
||
5431 |
} |
||
5432 |
|||
5433 |
struct mg_str mg_strdup_nul(const struct mg_str s) WEAK; |
||
5434 |
struct mg_str mg_strdup_nul(const struct mg_str s) { |
||
5435 |
return mg_strdup_common(s, 1 /* NUL-terminate */); |
||
5436 |
} |
||
5437 |
|||
5438 |
const char *mg_strchr(const struct mg_str s, int c) WEAK; |
||
5439 |
const char *mg_strchr(const struct mg_str s, int c) { |
||
5440 |
size_t i; |
||
5441 |
for (i = 0; i < s.len; i++) { |
||
5442 |
if (s.p[i] == c) return &s.p[i]; |
||
5443 |
} |
||
5444 |
return NULL; |
||
5445 |
} |
||
5446 |
|||
5447 |
int mg_strcmp(const struct mg_str str1, const struct mg_str str2) WEAK; |
||
5448 |
int mg_strcmp(const struct mg_str str1, const struct mg_str str2) { |
||
5449 |
size_t i = 0; |
||
5450 |
while (i < str1.len && i < str2.len) { |
||
5451 |
if (str1.p[i] < str2.p[i]) return -1; |
||
5452 |
if (str1.p[i] > str2.p[i]) return 1; |
||
5453 |
i++; |
||
5454 |
} |
||
5455 |
if (i < str1.len) return 1; |
||
5456 |
if (i < str2.len) return -1; |
||
5457 |
return 0; |
||
5458 |
} |
||
5459 |
|||
5460 |
int mg_strncmp(const struct mg_str, const struct mg_str, size_t n) WEAK; |
||
5461 |
int mg_strncmp(const struct mg_str str1, const struct mg_str str2, size_t n) { |
||
5462 |
struct mg_str s1 = str1; |
||
5463 |
struct mg_str s2 = str2; |
||
5464 |
|||
5465 |
if (s1.len > n) { |
||
5466 |
s1.len = n; |
||
5467 |
} |
||
5468 |
if (s2.len > n) { |
||
5469 |
s2.len = n; |
||
5470 |
} |
||
5471 |
return mg_strcmp(s1, s2); |
||
5472 |
} |
||
5473 |
|||
5474 |
const char *mg_strstr(const struct mg_str haystack, |
||
5475 |
const struct mg_str needle) WEAK; |
||
5476 |
const char *mg_strstr(const struct mg_str haystack, |
||
5477 |
const struct mg_str needle) { |
||
5478 |
size_t i; |
||
5479 |
if (needle.len > haystack.len) return NULL; |
||
5480 |
for (i = 0; i <= haystack.len - needle.len; i++) { |
||
5481 |
if (memcmp(haystack.p + i, needle.p, needle.len) == 0) { |
||
5482 |
return haystack.p + i; |
||
5483 |
} |
||
5484 |
} |
||
5485 |
return NULL; |
||
5486 |
} |
||
5487 |
#ifdef MJS_MODULE_LINES |
||
5488 |
#line 1 "common/str_util.c" |
||
5489 |
#endif |
||
5490 |
/* |
||
5491 |
* Copyright (c) 2014-2018 Cesanta Software Limited |
||
5492 |
* All rights reserved |
||
5493 |
* |
||
5494 |
* Licensed under the Apache License, Version 2.0 (the ""License""); |
||
5495 |
* you may not use this file except in compliance with the License. |
||
5496 |
* You may obtain a copy of the License at |
||
5497 |
* |
||
5498 |
* http://www.apache.org/licenses/LICENSE-2.0 |
||
5499 |
* |
||
5500 |
* Unless required by applicable law or agreed to in writing, software |
||
5501 |
* distributed under the License is distributed on an ""AS IS"" BASIS, |
||
5502 |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||
5503 |
* See the License for the specific language governing permissions and |
||
5504 |
* limitations under the License. |
||
5505 |
*/ |
||
5506 |
|||
5507 |
#ifndef EXCLUDE_COMMON |
||
5508 |
|||
5509 |
/* Amalgamated: #include "common/str_util.h" */ |
||
5510 |
/* Amalgamated: #include "common/mg_mem.h" */ |
||
5511 |
/* Amalgamated: #include "common/platform.h" */ |
||
5512 |
|||
5513 |
#ifndef C_DISABLE_BUILTIN_SNPRINTF |
||
5514 |
#define C_DISABLE_BUILTIN_SNPRINTF 0 |
||
5515 |
#endif |
||
5516 |
|||
5517 |
/* Amalgamated: #include "common/mg_mem.h" */ |
||
5518 |
|||
5519 |
size_t c_strnlen(const char *s, size_t maxlen) WEAK; |
||
5520 |
size_t c_strnlen(const char *s, size_t maxlen) { |
||
5521 |
size_t l = 0; |
||
5522 |
for (; l < maxlen && s[l] != '\0'; l++) { |
||
5523 |
} |
||
5524 |
return l; |
||
5525 |
} |
||
5526 |
|||
5527 |
#define C_SNPRINTF_APPEND_CHAR(ch) \ |
||
5528 |
do { \ |
||
5529 |
if (i < (int) buf_size) buf[i] = ch; \ |
||
5530 |
i++; \ |
||
5531 |
} while (0) |
||
5532 |
|||
5533 |
#define C_SNPRINTF_FLAG_ZERO 1 |
||
5534 |
|||
5535 |
#if C_DISABLE_BUILTIN_SNPRINTF |
||
5536 |
int c_vsnprintf(char *buf, size_t buf_size, const char *fmt, va_list ap) WEAK; |
||
5537 |
int c_vsnprintf(char *buf, size_t buf_size, const char *fmt, va_list ap) { |
||
5538 |
return vsnprintf(buf, buf_size, fmt, ap); |
||
5539 |
} |
||
5540 |
#else |
||
5541 |
static int c_itoa(char *buf, size_t buf_size, int64_t num, int base, int flags, |
||
5542 |
int field_width) { |
||
5543 |
char tmp[40]; |
||
5544 |
int i = 0, k = 0, neg = 0; |
||
5545 |
|||
5546 |
if (num < 0) { |
||
5547 |
neg++; |
||
5548 |
num = -num; |
||
5549 |
} |
||
5550 |
|||
5551 |
/* Print into temporary buffer - in reverse order */ |
||
5552 |
do { |
||
5553 |
int rem = num % base; |
||
5554 |
if (rem < 10) { |
||
5555 |
tmp[k++] = '0' + rem; |
||
5556 |
} else { |
||
5557 |
tmp[k++] = 'a' + (rem - 10); |
||
5558 |
} |
||
5559 |
num /= base; |
||
5560 |
} while (num > 0); |
||
5561 |
|||
5562 |
/* Zero padding */ |
||
5563 |
if (flags && C_SNPRINTF_FLAG_ZERO) { |
||
5564 |
while (k < field_width && k < (int) sizeof(tmp) - 1) { |
||
5565 |
tmp[k++] = '0'; |
||
5566 |
} |
||
5567 |
} |
||
5568 |
|||
5569 |
/* And sign */ |
||
5570 |
if (neg) { |
||
5571 |
tmp[k++] = '-'; |
||
5572 |
} |
||
5573 |
|||
5574 |
/* Now output */ |
||
5575 |
while (--k >= 0) { |
||
5576 |
C_SNPRINTF_APPEND_CHAR(tmp[k]); |
||
5577 |
} |
||
5578 |
|||
5579 |
return i; |
||
5580 |
} |
||
5581 |
|||
5582 |
int c_vsnprintf(char *buf, size_t buf_size, const char *fmt, va_list ap) WEAK; |
||
5583 |
int c_vsnprintf(char *buf, size_t buf_size, const char *fmt, va_list ap) { |
||
5584 |
int ch, i = 0, len_mod, flags, precision, field_width; |
||
5585 |
|||
5586 |
while ((ch = *fmt++) != '\0') { |
||
5587 |
if (ch != '%') { |
||
5588 |
C_SNPRINTF_APPEND_CHAR(ch); |
||
5589 |
} else { |
||
5590 |
/* |
||
5591 |
* Conversion specification: |
||
5592 |
* zero or more flags (one of: # 0 - <space> + ') |
||
5593 |
* an optional minimum field width (digits) |
||
5594 |
* an optional precision (. followed by digits, or *) |
||
5595 |
* an optional length modifier (one of: hh h l ll L q j z t) |
||
5596 |
* conversion specifier (one of: d i o u x X e E f F g G a A c s p n) |
||
5597 |
*/ |
||
5598 |
flags = field_width = precision = len_mod = 0; |
||
5599 |
|||
5600 |
/* Flags. only zero-pad flag is supported. */ |
||
5601 |
if (*fmt == '0') { |
||
5602 |
flags |= C_SNPRINTF_FLAG_ZERO; |
||
5603 |
} |
||
5604 |
|||
5605 |
/* Field width */ |
||
5606 |
while (*fmt >= '0' && *fmt <= '9') { |
||
5607 |
field_width *= 10; |
||
5608 |
field_width += *fmt++ - '0'; |
||
5609 |
} |
||
5610 |
/* Dynamic field width */ |
||
5611 |
if (*fmt == '*') { |
||
5612 |
field_width = va_arg(ap, int); |
||
5613 |
fmt++; |
||
5614 |
} |
||
5615 |
|||
5616 |
/* Precision */ |
||
5617 |
if (*fmt == '.') { |
||
5618 |
fmt++; |
||
5619 |
if (*fmt == '*') { |
||
5620 |
precision = va_arg(ap, int); |
||
5621 |
fmt++; |
||
5622 |
} else { |
||
5623 |
while (*fmt >= '0' && *fmt <= '9') { |
||
5624 |
precision *= 10; |
||
5625 |
precision += *fmt++ - '0'; |
||
5626 |
} |
||
5627 |
} |
||
5628 |
} |
||
5629 |
|||
5630 |
/* Length modifier */ |
||
5631 |
switch (*fmt) { |
||
5632 |
case 'h': |
||
5633 |
case 'l': |
||
5634 |
case 'L': |
||
5635 |
case 'I': |
||
5636 |
case 'q': |
||
5637 |
case 'j': |
||
5638 |
case 'z': |
||
5639 |
case 't': |
||
5640 |
len_mod = *fmt++; |
||
5641 |
if (*fmt == 'h') { |
||
5642 |
len_mod = 'H'; |
||
5643 |
fmt++; |
||
5644 |
} |
||
5645 |
if (*fmt == 'l') { |
||
5646 |
len_mod = 'q'; |
||
5647 |
fmt++; |
||
5648 |
} |
||
5649 |
break; |
||
5650 |
} |
||
5651 |
|||
5652 |
ch = *fmt++; |
||
5653 |
if (ch == 's') { |
||
5654 |
const char *s = va_arg(ap, const char *); /* Always fetch parameter */ |
||
5655 |
int j; |
||
5656 |
int pad = field_width - (precision >= 0 ? c_strnlen(s, precision) : 0); |
||
5657 |
for (j = 0; j < pad; j++) { |
||
5658 |
C_SNPRINTF_APPEND_CHAR(' '); |
||
5659 |
} |
||
5660 |
|||
5661 |
/* `s` may be NULL in case of %.*s */ |
||
5662 |
if (s != NULL) { |
||
5663 |
/* Ignore negative and 0 precisions */ |
||
5664 |
for (j = 0; (precision <= 0 || j < precision) && s[j] != '\0'; j++) { |
||
5665 |
C_SNPRINTF_APPEND_CHAR(s[j]); |
||
5666 |
} |
||
5667 |
} |
||
5668 |
} else if (ch == 'c') { |
||
5669 |
ch = va_arg(ap, int); /* Always fetch parameter */ |
||
5670 |
C_SNPRINTF_APPEND_CHAR(ch); |
||
5671 |
} else if (ch == 'd' && len_mod == 0) { |
||
5672 |
i += c_itoa(buf + i, buf_size - i, va_arg(ap, int), 10, flags, |
||
5673 |
field_width); |
||
5674 |
} else if (ch == 'd' && len_mod == 'l') { |
||
5675 |
i += c_itoa(buf + i, buf_size - i, va_arg(ap, long), 10, flags, |
||
5676 |
field_width); |
||
5677 |
#ifdef SSIZE_MAX |
||
5678 |
} else if (ch == 'd' && len_mod == 'z') { |
||
5679 |
i += c_itoa(buf + i, buf_size - i, va_arg(ap, ssize_t), 10, flags, |
||
5680 |
field_width); |
||
5681 |
#endif |
||
5682 |
} else if (ch == 'd' && len_mod == 'q') { |
||
5683 |
i += c_itoa(buf + i, buf_size - i, va_arg(ap, int64_t), 10, flags, |
||
5684 |
field_width); |
||
5685 |
} else if ((ch == 'x' || ch == 'u') && len_mod == 0) { |
||
5686 |
i += c_itoa(buf + i, buf_size - i, va_arg(ap, unsigned), |
||
5687 |
ch == 'x' ? 16 : 10, flags, field_width); |
||
5688 |
} else if ((ch == 'x' || ch == 'u') && len_mod == 'l') { |
||
5689 |
i += c_itoa(buf + i, buf_size - i, va_arg(ap, unsigned long), |
||
5690 |
ch == 'x' ? 16 : 10, flags, field_width); |
||
5691 |
} else if ((ch == 'x' || ch == 'u') && len_mod == 'z') { |
||
5692 |
i += c_itoa(buf + i, buf_size - i, va_arg(ap, size_t), |
||
5693 |
ch == 'x' ? 16 : 10, flags, field_width); |
||
5694 |
} else if (ch == 'p') { |
||
5695 |
unsigned long num = (unsigned long) (uintptr_t) va_arg(ap, void *); |
||
5696 |
C_SNPRINTF_APPEND_CHAR('0'); |
||
5697 |
C_SNPRINTF_APPEND_CHAR('x'); |
||
5698 |
i += c_itoa(buf + i, buf_size - i, num, 16, flags, 0); |
||
5699 |
} else { |
||
5700 |
#ifndef NO_LIBC |
||
5701 |
/* |
||
5702 |
* TODO(lsm): abort is not nice in a library, remove it |
||
5703 |
* Also, ESP8266 SDK doesn't have it |
||
5704 |
*/ |
||
5705 |
abort(); |
||
5706 |
#endif |
||
5707 |
} |
||
5708 |
} |
||
5709 |
} |
||
5710 |
|||
5711 |
/* Zero-terminate the result */ |
||
5712 |
if (buf_size > 0) { |
||
5713 |
buf[i < (int) buf_size ? i : (int) buf_size - 1] = '\0'; |
||
5714 |
} |
||
5715 |
|||
5716 |
return i; |
||
5717 |
} |
||
5718 |
#endif |
||
5719 |
|||
5720 |
int c_snprintf(char *buf, size_t buf_size, const char *fmt, ...) WEAK; |
||
5721 |
int c_snprintf(char *buf, size_t buf_size, const char *fmt, ...) { |
||
5722 |
int result; |
||
5723 |
va_list ap; |
||
5724 |
va_start(ap, fmt); |
||
5725 |
result = c_vsnprintf(buf, buf_size, fmt, ap); |
||
5726 |
va_end(ap); |
||
5727 |
return result; |
||
5728 |
} |
||
5729 |
|||
5730 |
#ifdef _WIN32 |
||
5731 |
int to_wchar(const char *path, wchar_t *wbuf, size_t wbuf_len) { |
||
5732 |
int ret; |
||
5733 |
char buf[MAX_PATH * 2], buf2[MAX_PATH * 2], *p; |
||
5734 |
|||
5735 |
strncpy(buf, path, sizeof(buf)); |
||
5736 |
buf[sizeof(buf) - 1] = '\0'; |
||
5737 |
|||
5738 |
/* Trim trailing slashes. Leave backslash for paths like "X:\" */ |
||
5739 |
p = buf + strlen(buf) - 1; |
||
5740 |
while (p > buf && p[-1] != ':' && (p[0] == '\\' || p[0] == '/')) *p-- = '\0'; |
||
5741 |
|||
5742 |
memset(wbuf, 0, wbuf_len * sizeof(wchar_t)); |
||
5743 |
ret = MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, (int) wbuf_len); |
||
5744 |
|||
5745 |
/* |
||
5746 |
* Convert back to Unicode. If doubly-converted string does not match the |
||
5747 |
* original, something is fishy, reject. |
||
5748 |
*/ |
||
5749 |
WideCharToMultiByte(CP_UTF8, 0, wbuf, (int) wbuf_len, buf2, sizeof(buf2), |
||
5750 |
NULL, NULL); |
||
5751 |
if (strcmp(buf, buf2) != 0) { |
||
5752 |
wbuf[0] = L'\0'; |
||
5753 |
ret = 0; |
||
5754 |
} |
||
5755 |
|||
5756 |
return ret; |
||
5757 |
} |
||
5758 |
#endif /* _WIN32 */ |
||
5759 |
|||
5760 |
/* The simplest O(mn) algorithm. Better implementation are GPLed */ |
||
5761 |
const char *c_strnstr(const char *s, const char *find, size_t slen) WEAK; |
||
5762 |
const char *c_strnstr(const char *s, const char *find, size_t slen) { |
||
5763 |
size_t find_length = strlen(find); |
||
5764 |
size_t i; |
||
5765 |
|||
5766 |
for (i = 0; i < slen; i++) { |
||
5767 |
if (i + find_length > slen) { |
||
5768 |
return NULL; |
||
5769 |
} |
||
5770 |
|||
5771 |
if (strncmp(&s[i], find, find_length) == 0) { |
||
5772 |
return &s[i]; |
||
5773 |
} |
||
5774 |
} |
||
5775 |
|||
5776 |
return NULL; |
||
5777 |
} |
||
5778 |
|||
5779 |
#if CS_ENABLE_STRDUP |
||
5780 |
char *strdup(const char *src) WEAK; |
||
5781 |
char *strdup(const char *src) { |
||
5782 |
size_t len = strlen(src) + 1; |
||
5783 |
char *ret = MG_MALLOC(len); |
||
5784 |
if (ret != NULL) { |
||
5785 |
strcpy(ret, src); |
||
5786 |
} |
||
5787 |
return ret; |
||
5788 |
} |
||
5789 |
#endif |
||
5790 |
|||
5791 |
void cs_to_hex(char *to, const unsigned char *p, size_t len) WEAK; |
||
5792 |
void cs_to_hex(char *to, const unsigned char *p, size_t len) { |
||
5793 |
static const char *hex = "0123456789abcdef"; |
||
5794 |
|||
5795 |
for (; len--; p++) { |
||
5796 |
*to++ = hex[p[0] >> 4]; |
||
5797 |
*to++ = hex[p[0] & 0x0f]; |
||
5798 |
} |
||
5799 |
*to = '\0'; |
||
5800 |
} |
||
5801 |
|||
5802 |
static int fourbit(int ch) { |
||
5803 |
if (ch >= '0' && ch <= '9') { |
||
5804 |
return ch - '0'; |
||
5805 |
} else if (ch >= 'a' && ch <= 'f') { |
||
5806 |
return ch - 'a' + 10; |
||
5807 |
} else if (ch >= 'A' && ch <= 'F') { |
||
5808 |
return ch - 'A' + 10; |
||
5809 |
} |
||
5810 |
return 0; |
||
5811 |
} |
||
5812 |
|||
5813 |
void cs_from_hex(char *to, const char *p, size_t len) WEAK; |
||
5814 |
void cs_from_hex(char *to, const char *p, size_t len) { |
||
5815 |
size_t i; |
||
5816 |
|||
5817 |
for (i = 0; i < len; i += 2) { |
||
5818 |
*to++ = (fourbit(p[i]) << 4) + fourbit(p[i + 1]); |
||
5819 |
} |
||
5820 |
*to = '\0'; |
||
5821 |
} |
||
5822 |
|||
5823 |
#if CS_ENABLE_TO64 |
||
5824 |
int64_t cs_to64(const char *s) WEAK; |
||
5825 |
int64_t cs_to64(const char *s) { |
||
5826 |
int64_t result = 0; |
||
5827 |
int64_t neg = 1; |
||
5828 |
while (*s && isspace((unsigned char) *s)) s++; |
||
5829 |
if (*s == '-') { |
||
5830 |
neg = -1; |
||
5831 |
s++; |
||
5832 |
} |
||
5833 |
while (isdigit((unsigned char) *s)) { |
||
5834 |
result *= 10; |
||
5835 |
result += (*s - '0'); |
||
5836 |
s++; |
||
5837 |
} |
||
5838 |
return result * neg; |
||
5839 |
} |
||
5840 |
#endif |
||
5841 |
|||
5842 |
static int str_util_lowercase(const char *s) { |
||
5843 |
return tolower(*(const unsigned char *) s); |
||
5844 |
} |
||
5845 |
|||
5846 |
int mg_ncasecmp(const char *s1, const char *s2, size_t len) WEAK; |
||
5847 |
int mg_ncasecmp(const char *s1, const char *s2, size_t len) { |
||
5848 |
int diff = 0; |
||
5849 |
|||
5850 |
if (len > 0) do { |
||
5851 |
diff = str_util_lowercase(s1++) - str_util_lowercase(s2++); |
||
5852 |
} while (diff == 0 && s1[-1] != '\0' && --len > 0); |
||
5853 |
|||
5854 |
return diff; |
||
5855 |
} |
||
5856 |
|||
5857 |
int mg_casecmp(const char *s1, const char *s2) WEAK; |
||
5858 |
int mg_casecmp(const char *s1, const char *s2) { |
||
5859 |
return mg_ncasecmp(s1, s2, (size_t) ~0); |
||
5860 |
} |
||
5861 |
|||
5862 |
int mg_asprintf(char **buf, size_t size, const char *fmt, ...) WEAK; |
||
5863 |
50 |
int mg_asprintf(char **buf, size_t size, const char *fmt, ...) { |
|
5864 |
int ret; |
||
5865 |
va_list ap; |
||
5866 |
50 |
va_start(ap, fmt); |
|
5867 |
50 |
ret = mg_avprintf(buf, size, fmt, ap); |
|
5868 |
50 |
va_end(ap); |
|
5869 |
50 |
return ret; |
|
5870 |
} |
||
5871 |
|||
5872 |
int mg_avprintf(char **buf, size_t size, const char *fmt, va_list ap) WEAK; |
||
5873 |
112 |
int mg_avprintf(char **buf, size_t size, const char *fmt, va_list ap) { |
|
5874 |
va_list ap_copy; |
||
5875 |
int len; |
||
5876 |
|||
5877 |
112 |
va_copy(ap_copy, ap); |
|
5878 |
112 |
len = vsnprintf(*buf, size, fmt, ap_copy); |
|
5879 |
112 |
va_end(ap_copy); |
|
5880 |
|||
5881 |
✗✓ | 112 |
if (len < 0) { |
5882 |
/* eCos and Windows are not standard-compliant and return -1 when |
||
5883 |
* the buffer is too small. Keep allocating larger buffers until we |
||
5884 |
* succeed or out of memory. */ |
||
5885 |
*buf = NULL; /* LCOV_EXCL_START */ |
||
5886 |
while (len < 0) { |
||
5887 |
MG_FREE(*buf); |
||
5888 |
if (size == 0) { |
||
5889 |
size = 5; |
||
5890 |
} |
||
5891 |
size *= 2; |
||
5892 |
if ((*buf = (char *) MG_MALLOC(size)) == NULL) { |
||
5893 |
len = -1; |
||
5894 |
break; |
||
5895 |
} |
||
5896 |
va_copy(ap_copy, ap); |
||
5897 |
len = vsnprintf(*buf, size - 1, fmt, ap_copy); |
||
5898 |
va_end(ap_copy); |
||
5899 |
} |
||
5900 |
|||
5901 |
/* |
||
5902 |
* Microsoft version of vsnprintf() is not always null-terminated, so put |
||
5903 |
* the terminator manually |
||
5904 |
*/ |
||
5905 |
(*buf)[len] = 0; |
||
5906 |
/* LCOV_EXCL_STOP */ |
||
5907 |
✓✗ | 112 |
} else if (len >= (int) size) { |
5908 |
/* Standard-compliant code path. Allocate a buffer that is large enough. */ |
||
5909 |
✗✓ | 112 |
if ((*buf = (char *) MG_MALLOC(len + 1)) == NULL) { |
5910 |
len = -1; /* LCOV_EXCL_LINE */ |
||
5911 |
} else { /* LCOV_EXCL_LINE */ |
||
5912 |
112 |
va_copy(ap_copy, ap); |
|
5913 |
112 |
len = vsnprintf(*buf, len + 1, fmt, ap_copy); |
|
5914 |
112 |
va_end(ap_copy); |
|
5915 |
} |
||
5916 |
} |
||
5917 |
|||
5918 |
112 |
return len; |
|
5919 |
} |
||
5920 |
|||
5921 |
const char *mg_next_comma_list_entry(const char *, struct mg_str *, |
||
5922 |
struct mg_str *) WEAK; |
||
5923 |
const char *mg_next_comma_list_entry(const char *list, struct mg_str *val, |
||
5924 |
struct mg_str *eq_val) { |
||
5925 |
struct mg_str ret = mg_next_comma_list_entry_n(mg_mk_str(list), val, eq_val); |
||
5926 |
return ret.p; |
||
5927 |
} |
||
5928 |
|||
5929 |
struct mg_str mg_next_comma_list_entry_n(struct mg_str list, struct mg_str *val, |
||
5930 |
struct mg_str *eq_val) WEAK; |
||
5931 |
struct mg_str mg_next_comma_list_entry_n(struct mg_str list, struct mg_str *val, |
||
5932 |
struct mg_str *eq_val) { |
||
5933 |
if (list.len == 0) { |
||
5934 |
/* End of the list */ |
||
5935 |
list = mg_mk_str(NULL); |
||
5936 |
} else { |
||
5937 |
const char *chr = NULL; |
||
5938 |
*val = list; |
||
5939 |
|||
5940 |
if ((chr = mg_strchr(*val, ',')) != NULL) { |
||
5941 |
/* Comma found. Store length and shift the list ptr */ |
||
5942 |
val->len = chr - val->p; |
||
5943 |
chr++; |
||
5944 |
list.len -= (chr - list.p); |
||
5945 |
list.p = chr; |
||
5946 |
} else { |
||
5947 |
/* This value is the last one */ |
||
5948 |
list = mg_mk_str_n(list.p + list.len, 0); |
||
5949 |
} |
||
5950 |
|||
5951 |
if (eq_val != NULL) { |
||
5952 |
/* Value has form "x=y", adjust pointers and lengths */ |
||
5953 |
/* so that val points to "x", and eq_val points to "y". */ |
||
5954 |
eq_val->len = 0; |
||
5955 |
eq_val->p = (const char *) memchr(val->p, '=', val->len); |
||
5956 |
if (eq_val->p != NULL) { |
||
5957 |
eq_val->p++; /* Skip over '=' character */ |
||
5958 |
eq_val->len = val->p + val->len - eq_val->p; |
||
5959 |
val->len = (eq_val->p - val->p) - 1; |
||
5960 |
} |
||
5961 |
} |
||
5962 |
} |
||
5963 |
|||
5964 |
return list; |
||
5965 |
} |
||
5966 |
|||
5967 |
size_t mg_match_prefix_n(const struct mg_str, const struct mg_str) WEAK; |
||
5968 |
size_t mg_match_prefix_n(const struct mg_str pattern, const struct mg_str str) { |
||
5969 |
const char *or_str; |
||
5970 |
size_t res = 0, len = 0, i = 0, j = 0; |
||
5971 |
|||
5972 |
if ((or_str = (const char *) memchr(pattern.p, '|', pattern.len)) != NULL || |
||
5973 |
(or_str = (const char *) memchr(pattern.p, ',', pattern.len)) != NULL) { |
||
5974 |
struct mg_str pstr = {pattern.p, (size_t)(or_str - pattern.p)}; |
||
5975 |
res = mg_match_prefix_n(pstr, str); |
||
5976 |
if (res > 0) return res; |
||
5977 |
pstr.p = or_str + 1; |
||
5978 |
pstr.len = (pattern.p + pattern.len) - (or_str + 1); |
||
5979 |
return mg_match_prefix_n(pstr, str); |
||
5980 |
} |
||
5981 |
|||
5982 |
for (; i < pattern.len && j < str.len; i++, j++) { |
||
5983 |
if (pattern.p[i] == '?') { |
||
5984 |
continue; |
||
5985 |
} else if (pattern.p[i] == '*') { |
||
5986 |
i++; |
||
5987 |
if (i < pattern.len && pattern.p[i] == '*') { |
||
5988 |
i++; |
||
5989 |
len = str.len - j; |
||
5990 |
} else { |
||
5991 |
len = 0; |
||
5992 |
while (j + len < str.len && str.p[j + len] != '/') len++; |
||
5993 |
} |
||
5994 |
if (i == pattern.len || (pattern.p[i] == '$' && i == pattern.len - 1)) |
||
5995 |
return j + len; |
||
5996 |
do { |
||
5997 |
const struct mg_str pstr = {pattern.p + i, pattern.len - i}; |
||
5998 |
const struct mg_str sstr = {str.p + j + len, str.len - j - len}; |
||
5999 |
res = mg_match_prefix_n(pstr, sstr); |
||
6000 |
} while (res == 0 && len != 0 && len-- > 0); |
||
6001 |
return res == 0 ? 0 : j + res + len; |
||
6002 |
} else if (str_util_lowercase(&pattern.p[i]) != |
||
6003 |
str_util_lowercase(&str.p[j])) { |
||
6004 |
break; |
||
6005 |
} |
||
6006 |
} |
||
6007 |
if (i < pattern.len && pattern.p[i] == '$') { |
||
6008 |
return j == str.len ? str.len : 0; |
||
6009 |
} |
||
6010 |
return i == pattern.len ? j : 0; |
||
6011 |
} |
||
6012 |
|||
6013 |
size_t mg_match_prefix(const char *, int, const char *) WEAK; |
||
6014 |
size_t mg_match_prefix(const char *pattern, int pattern_len, const char *str) { |
||
6015 |
const struct mg_str pstr = {pattern, (size_t) pattern_len}; |
||
6016 |
struct mg_str s = {str, 0}; |
||
6017 |
if (str != NULL) s.len = strlen(str); |
||
6018 |
return mg_match_prefix_n(pstr, s); |
||
6019 |
} |
||
6020 |
|||
6021 |
#endif /* EXCLUDE_COMMON */ |
||
6022 |
#ifdef MJS_MODULE_LINES |
||
6023 |
#line 1 "frozen/frozen.c" |
||
6024 |
#endif |
||
6025 |
/* |
||
6026 |
* Copyright (c) 2004-2013 Sergey Lyubka <valenok@gmail.com> |
||
6027 |
* Copyright (c) 2018 Cesanta Software Limited |
||
6028 |
* All rights reserved |
||
6029 |
* |
||
6030 |
* Licensed under the Apache License, Version 2.0 (the ""License""); |
||
6031 |
* you may not use this file except in compliance with the License. |
||
6032 |
* You may obtain a copy of the License at |
||
6033 |
* |
||
6034 |
* http://www.apache.org/licenses/LICENSE-2.0 |
||
6035 |
* |
||
6036 |
* Unless required by applicable law or agreed to in writing, software |
||
6037 |
* distributed under the License is distributed on an ""AS IS"" BASIS, |
||
6038 |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||
6039 |
* See the License for the specific language governing permissions and |
||
6040 |
* limitations under the License. |
||
6041 |
*/ |
||
6042 |
|||
6043 |
#define _CRT_SECURE_NO_WARNINGS /* Disable deprecation warning in VS2005+ */ |
||
6044 |
|||
6045 |
/* Amalgamated: #include "frozen.h" */ |
||
6046 |
#include <ctype.h> |
||
6047 |
#include <stdarg.h> |
||
6048 |
#include <stdio.h> |
||
6049 |
#include <stdlib.h> |
||
6050 |
#include <string.h> |
||
6051 |
|||
6052 |
#if !defined(WEAK) |
||
6053 |
#if (defined(__GNUC__) || defined(__TI_COMPILER_VERSION__)) && !defined(_WIN32) |
||
6054 |
#define WEAK __attribute__((weak)) |
||
6055 |
#else |
||
6056 |
#define WEAK |
||
6057 |
#endif |
||
6058 |
#endif |
||
6059 |
|||
6060 |
#ifdef _WIN32 |
||
6061 |
#undef snprintf |
||
6062 |
#undef vsnprintf |
||
6063 |
#define snprintf cs_win_snprintf |
||
6064 |
#define vsnprintf cs_win_vsnprintf |
||
6065 |
int cs_win_snprintf(char *str, size_t size, const char *format, ...); |
||
6066 |
int cs_win_vsnprintf(char *str, size_t size, const char *format, va_list ap); |
||
6067 |
#if _MSC_VER >= 1700 |
||
6068 |
#include <stdint.h> |
||
6069 |
#else |
||
6070 |
typedef _int64 int64_t; |
||
6071 |
typedef unsigned _int64 uint64_t; |
||
6072 |
#endif |
||
6073 |
#define PRId64 "I64d" |
||
6074 |
#define PRIu64 "I64u" |
||
6075 |
#else /* _WIN32 */ |
||
6076 |
/* <inttypes.h> wants this for C++ */ |
||
6077 |
#ifndef __STDC_FORMAT_MACROS |
||
6078 |
#define __STDC_FORMAT_MACROS |
||
6079 |
#endif |
||
6080 |
#include <inttypes.h> |
||
6081 |
#endif /* _WIN32 */ |
||
6082 |
|||
6083 |
#ifndef INT64_FMT |
||
6084 |
#define INT64_FMT PRId64 |
||
6085 |
#endif |
||
6086 |
#ifndef UINT64_FMT |
||
6087 |
#define UINT64_FMT PRIu64 |
||
6088 |
#endif |
||
6089 |
|||
6090 |
#ifndef va_copy |
||
6091 |
#define va_copy(x, y) x = y |
||
6092 |
#endif |
||
6093 |
|||
6094 |
#ifndef JSON_MAX_PATH_LEN |
||
6095 |
#define JSON_MAX_PATH_LEN 256 |
||
6096 |
#endif |
||
6097 |
|||
6098 |
struct frozen { |
||
6099 |
const char *end; |
||
6100 |
const char *cur; |
||
6101 |
|||
6102 |
const char *cur_name; |
||
6103 |
size_t cur_name_len; |
||
6104 |
|||
6105 |
/* For callback API */ |
||
6106 |
char path[JSON_MAX_PATH_LEN]; |
||
6107 |
size_t path_len; |
||
6108 |
void *callback_data; |
||
6109 |
json_walk_callback_t callback; |
||
6110 |
}; |
||
6111 |
|||
6112 |
struct fstate { |
||
6113 |
const char *ptr; |
||
6114 |
size_t path_len; |
||
6115 |
}; |
||
6116 |
|||
6117 |
#define SET_STATE(fr, ptr, str, len) \ |
||
6118 |
struct fstate fstate = {(ptr), (fr)->path_len}; \ |
||
6119 |
append_to_path((fr), (str), (len)); |
||
6120 |
|||
6121 |
#define CALL_BACK(fr, tok, value, len) \ |
||
6122 |
do { \ |
||
6123 |
if ((fr)->callback && \ |
||
6124 |
((fr)->path_len == 0 || (fr)->path[(fr)->path_len - 1] != '.')) { \ |
||
6125 |
struct json_token t = {(value), (len), (tok)}; \ |
||
6126 |
\ |
||
6127 |
/* Call the callback with the given value and current name */ \ |
||
6128 |
(fr)->callback((fr)->callback_data, (fr)->cur_name, (fr)->cur_name_len, \ |
||
6129 |
(fr)->path, &t); \ |
||
6130 |
\ |
||
6131 |
/* Reset the name */ \ |
||
6132 |
(fr)->cur_name = NULL; \ |
||
6133 |
(fr)->cur_name_len = 0; \ |
||
6134 |
} \ |
||
6135 |
} while (0) |
||
6136 |
|||
6137 |
static int append_to_path(struct frozen *f, const char *str, int size) { |
||
6138 |
int n = f->path_len; |
||
6139 |
int left = sizeof(f->path) - n - 1; |
||
6140 |
if (size > left) size = left; |
||
6141 |
memcpy(f->path + n, str, size); |
||
6142 |
f->path[n + size] = '\0'; |
||
6143 |
f->path_len += size; |
||
6144 |
return n; |
||
6145 |
} |
||
6146 |
|||
6147 |
static void truncate_path(struct frozen *f, size_t len) { |
||
6148 |
f->path_len = len; |
||
6149 |
f->path[len] = '\0'; |
||
6150 |
} |
||
6151 |
|||
6152 |
static int parse_object(struct frozen *f); |
||
6153 |
static int parse_value(struct frozen *f); |
||
6154 |
|||
6155 |
#define EXPECT(cond, err_code) \ |
||
6156 |
do { \ |
||
6157 |
if (!(cond)) return (err_code); \ |
||
6158 |
} while (0) |
||
6159 |
|||
6160 |
#define TRY(expr) \ |
||
6161 |
do { \ |
||
6162 |
int _n = expr; \ |
||
6163 |
if (_n < 0) return _n; \ |
||
6164 |
} while (0) |
||
6165 |
|||
6166 |
#define END_OF_STRING (-1) |
||
6167 |
|||
6168 |
static int left(const struct frozen *f) { |
||
6169 |
return f->end - f->cur; |
||
6170 |
} |
||
6171 |
|||
6172 |
static int is_space(int ch) { |
||
6173 |
return ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n'; |
||
6174 |
} |
||
6175 |
|||
6176 |
static void skip_whitespaces(struct frozen *f) { |
||
6177 |
while (f->cur < f->end && is_space(*f->cur)) f->cur++; |
||
6178 |
} |
||
6179 |
|||
6180 |
static int cur(struct frozen *f) { |
||
6181 |
skip_whitespaces(f); |
||
6182 |
return f->cur >= f->end ? END_OF_STRING : *(unsigned char *) f->cur; |
||
6183 |
} |
||
6184 |
|||
6185 |
static int test_and_skip(struct frozen *f, int expected) { |
||
6186 |
int ch = cur(f); |
||
6187 |
if (ch == expected) { |
||
6188 |
f->cur++; |
||
6189 |
return 0; |
||
6190 |
} |
||
6191 |
return ch == END_OF_STRING ? JSON_STRING_INCOMPLETE : JSON_STRING_INVALID; |
||
6192 |
} |
||
6193 |
|||
6194 |
static int is_alpha(int ch) { |
||
6195 |
return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'); |
||
6196 |
} |
||
6197 |
|||
6198 |
static int is_digit(int ch) { |
||
6199 |
return ch >= '0' && ch <= '9'; |
||
6200 |
} |
||
6201 |
|||
6202 |
static int is_hex_digit(int ch) { |
||
6203 |
return is_digit(ch) || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F'); |
||
6204 |
} |
||
6205 |
|||
6206 |
static int get_escape_len(const char *s, int len) { |
||
6207 |
switch (*s) { |
||
6208 |
case 'u': |
||
6209 |
return len < 6 ? JSON_STRING_INCOMPLETE |
||
6210 |
: is_hex_digit(s[1]) && is_hex_digit(s[2]) && |
||
6211 |
is_hex_digit(s[3]) && is_hex_digit(s[4]) |
||
6212 |
? 5 |
||
6213 |
: JSON_STRING_INVALID; |
||
6214 |
case '"': |
||
6215 |
case '\\': |
||
6216 |
case '/': |
||
6217 |
case 'b': |
||
6218 |
case 'f': |
||
6219 |
case 'n': |
||
6220 |
case 'r': |
||
6221 |
case 't': |
||
6222 |
return len < 2 ? JSON_STRING_INCOMPLETE : 1; |
||
6223 |
default: |
||
6224 |
return JSON_STRING_INVALID; |
||
6225 |
} |
||
6226 |
} |
||
6227 |
|||
6228 |
/* identifier = letter { letter | digit | '_' } */ |
||
6229 |
static int parse_identifier(struct frozen *f) { |
||
6230 |
EXPECT(is_alpha(cur(f)), JSON_STRING_INVALID); |
||
6231 |
{ |
||
6232 |
SET_STATE(f, f->cur, "", 0); |
||
6233 |
while (f->cur < f->end && |
||
6234 |
(*f->cur == '_' || is_alpha(*f->cur) || is_digit(*f->cur))) { |
||
6235 |
f->cur++; |
||
6236 |
} |
||
6237 |
truncate_path(f, fstate.path_len); |
||
6238 |
CALL_BACK(f, JSON_TYPE_STRING, fstate.ptr, f->cur - fstate.ptr); |
||
6239 |
} |
||
6240 |
return 0; |
||
6241 |
} |
||
6242 |
|||
6243 |
static int get_utf8_char_len(unsigned char ch) { |
||
6244 |
if ((ch & 0x80) == 0) return 1; |
||
6245 |
switch (ch & 0xf0) { |
||
6246 |
case 0xf0: |
||
6247 |
return 4; |
||
6248 |
case 0xe0: |
||
6249 |
return 3; |
||
6250 |
default: |
||
6251 |
return 2; |
||
6252 |
} |
||
6253 |
} |
||
6254 |
|||
6255 |
/* string = '"' { quoted_printable_chars } '"' */ |
||
6256 |
static int parse_string(struct frozen *f) { |
||
6257 |
int n, ch = 0, len = 0; |
||
6258 |
TRY(test_and_skip(f, '"')); |
||
6259 |
{ |
||
6260 |
SET_STATE(f, f->cur, "", 0); |
||
6261 |
for (; f->cur < f->end; f->cur += len) { |
||
6262 |
ch = *(unsigned char *) f->cur; |
||
6263 |
len = get_utf8_char_len((unsigned char) ch); |
||
6264 |
EXPECT(ch >= 32 && len > 0, JSON_STRING_INVALID); /* No control chars */ |
||
6265 |
EXPECT(len <= left(f), JSON_STRING_INCOMPLETE); |
||
6266 |
if (ch == '\\') { |
||
6267 |
EXPECT((n = get_escape_len(f->cur + 1, left(f))) > 0, n); |
||
6268 |
len += n; |
||
6269 |
} else if (ch == '"') { |
||
6270 |
truncate_path(f, fstate.path_len); |
||
6271 |
CALL_BACK(f, JSON_TYPE_STRING, fstate.ptr, f->cur - fstate.ptr); |
||
6272 |
f->cur++; |
||
6273 |
break; |
||
6274 |
}; |
||
6275 |
} |
||
6276 |
} |
||
6277 |
return ch == '"' ? 0 : JSON_STRING_INCOMPLETE; |
||
6278 |
} |
||
6279 |
|||
6280 |
/* number = [ '-' ] digit+ [ '.' digit+ ] [ ['e'|'E'] ['+'|'-'] digit+ ] */ |
||
6281 |
static int parse_number(struct frozen *f) { |
||
6282 |
int ch = cur(f); |
||
6283 |
SET_STATE(f, f->cur, "", 0); |
||
6284 |
if (ch == '-') f->cur++; |
||
6285 |
EXPECT(f->cur < f->end, JSON_STRING_INCOMPLETE); |
||
6286 |
EXPECT(is_digit(f->cur[0]), JSON_STRING_INVALID); |
||
6287 |
while (f->cur < f->end && is_digit(f->cur[0])) f->cur++; |
||
6288 |
if (f->cur < f->end && f->cur[0] == '.') { |
||
6289 |
f->cur++; |
||
6290 |
EXPECT(f->cur < f->end, JSON_STRING_INCOMPLETE); |
||
6291 |
EXPECT(is_digit(f->cur[0]), JSON_STRING_INVALID); |
||
6292 |
while (f->cur < f->end && is_digit(f->cur[0])) f->cur++; |
||
6293 |
} |
||
6294 |
if (f->cur < f->end && (f->cur[0] == 'e' || f->cur[0] == 'E')) { |
||
6295 |
f->cur++; |
||
6296 |
EXPECT(f->cur < f->end, JSON_STRING_INCOMPLETE); |
||
6297 |
if ((f->cur[0] == '+' || f->cur[0] == '-')) f->cur++; |
||
6298 |
EXPECT(f->cur < f->end, JSON_STRING_INCOMPLETE); |
||
6299 |
EXPECT(is_digit(f->cur[0]), JSON_STRING_INVALID); |
||
6300 |
while (f->cur < f->end && is_digit(f->cur[0])) f->cur++; |
||
6301 |
} |
||
6302 |
truncate_path(f, fstate.path_len); |
||
6303 |
CALL_BACK(f, JSON_TYPE_NUMBER, fstate.ptr, f->cur - fstate.ptr); |
||
6304 |
return 0; |
||
6305 |
} |
||
6306 |
|||
6307 |
/* array = '[' [ value { ',' value } ] ']' */ |
||
6308 |
static int parse_array(struct frozen *f) { |
||
6309 |
int i = 0, current_path_len; |
||
6310 |
char buf[20]; |
||
6311 |
CALL_BACK(f, JSON_TYPE_ARRAY_START, NULL, 0); |
||
6312 |
TRY(test_and_skip(f, '[')); |
||
6313 |
{ |
||
6314 |
{ |
||
6315 |
SET_STATE(f, f->cur - 1, "", 0); |
||
6316 |
while (cur(f) != ']') { |
||
6317 |
snprintf(buf, sizeof(buf), "[%d]", i); |
||
6318 |
i++; |
||
6319 |
current_path_len = append_to_path(f, buf, strlen(buf)); |
||
6320 |
f->cur_name = |
||
6321 |
f->path + strlen(f->path) - strlen(buf) + 1 /*opening brace*/; |
||
6322 |
f->cur_name_len = strlen(buf) - 2 /*braces*/; |
||
6323 |
TRY(parse_value(f)); |
||
6324 |
truncate_path(f, current_path_len); |
||
6325 |
if (cur(f) == ',') f->cur++; |
||
6326 |
} |
||
6327 |
TRY(test_and_skip(f, ']')); |
||
6328 |
truncate_path(f, fstate.path_len); |
||
6329 |
CALL_BACK(f, JSON_TYPE_ARRAY_END, fstate.ptr, f->cur - fstate.ptr); |
||
6330 |
} |
||
6331 |
} |
||
6332 |
return 0; |
||
6333 |
} |
||
6334 |
|||
6335 |
static int expect(struct frozen *f, const char *s, int len, |
||
6336 |
enum json_token_type tok_type) { |
||
6337 |
int i, n = left(f); |
||
6338 |
SET_STATE(f, f->cur, "", 0); |
||
6339 |
for (i = 0; i < len; i++) { |
||
6340 |
if (i >= n) return JSON_STRING_INCOMPLETE; |
||
6341 |
if (f->cur[i] != s[i]) return JSON_STRING_INVALID; |
||
6342 |
} |
||
6343 |
f->cur += len; |
||
6344 |
truncate_path(f, fstate.path_len); |
||
6345 |
|||
6346 |
CALL_BACK(f, tok_type, fstate.ptr, f->cur - fstate.ptr); |
||
6347 |
|||
6348 |
return 0; |
||
6349 |
} |
||
6350 |
|||
6351 |
/* value = 'null' | 'true' | 'false' | number | string | array | object */ |
||
6352 |
static int parse_value(struct frozen *f) { |
||
6353 |
int ch = cur(f); |
||
6354 |
|||
6355 |
switch (ch) { |
||
6356 |
case '"': |
||
6357 |
TRY(parse_string(f)); |
||
6358 |
break; |
||
6359 |
case '{': |
||
6360 |
TRY(parse_object(f)); |
||
6361 |
break; |
||
6362 |
case '[': |
||
6363 |
TRY(parse_array(f)); |
||
6364 |
break; |
||
6365 |
case 'n': |
||
6366 |
TRY(expect(f, "null", 4, JSON_TYPE_NULL)); |
||
6367 |
break; |
||
6368 |
case 't': |
||
6369 |
TRY(expect(f, "true", 4, JSON_TYPE_TRUE)); |
||
6370 |
break; |
||
6371 |
case 'f': |
||
6372 |
TRY(expect(f, "false", 5, JSON_TYPE_FALSE)); |
||
6373 |
break; |
||
6374 |
case '-': |
||
6375 |
case '0': |
||
6376 |
case '1': |
||
6377 |
case '2': |
||
6378 |
case '3': |
||
6379 |
case '4': |
||
6380 |
case '5': |
||
6381 |
case '6': |
||
6382 |
case '7': |
||
6383 |
case '8': |
||
6384 |
case '9': |
||
6385 |
TRY(parse_number(f)); |
||
6386 |
break; |
||
6387 |
default: |
||
6388 |
return ch == END_OF_STRING ? JSON_STRING_INCOMPLETE : JSON_STRING_INVALID; |
||
6389 |
} |
||
6390 |
|||
6391 |
return 0; |
||
6392 |
} |
||
6393 |
|||
6394 |
/* key = identifier | string */ |
||
6395 |
static int parse_key(struct frozen *f) { |
||
6396 |
int ch = cur(f); |
||
6397 |
if (is_alpha(ch)) { |
||
6398 |
TRY(parse_identifier(f)); |
||
6399 |
} else if (ch == '"') { |
||
6400 |
TRY(parse_string(f)); |
||
6401 |
} else { |
||
6402 |
return ch == END_OF_STRING ? JSON_STRING_INCOMPLETE : JSON_STRING_INVALID; |
||
6403 |
} |
||
6404 |
return 0; |
||
6405 |
} |
||
6406 |
|||
6407 |
/* pair = key ':' value */ |
||
6408 |
static int parse_pair(struct frozen *f) { |
||
6409 |
int current_path_len; |
||
6410 |
const char *tok; |
||
6411 |
skip_whitespaces(f); |
||
6412 |
tok = f->cur; |
||
6413 |
TRY(parse_key(f)); |
||
6414 |
{ |
||
6415 |
f->cur_name = *tok == '"' ? tok + 1 : tok; |
||
6416 |
f->cur_name_len = *tok == '"' ? f->cur - tok - 2 : f->cur - tok; |
||
6417 |
current_path_len = append_to_path(f, f->cur_name, f->cur_name_len); |
||
6418 |
} |
||
6419 |
TRY(test_and_skip(f, ':')); |
||
6420 |
TRY(parse_value(f)); |
||
6421 |
truncate_path(f, current_path_len); |
||
6422 |
return 0; |
||
6423 |
} |
||
6424 |
|||
6425 |
/* object = '{' pair { ',' pair } '}' */ |
||
6426 |
static int parse_object(struct frozen *f) { |
||
6427 |
CALL_BACK(f, JSON_TYPE_OBJECT_START, NULL, 0); |
||
6428 |
TRY(test_and_skip(f, '{')); |
||
6429 |
{ |
||
6430 |
SET_STATE(f, f->cur - 1, ".", 1); |
||
6431 |
while (cur(f) != '}') { |
||
6432 |
TRY(parse_pair(f)); |
||
6433 |
if (cur(f) == ',') f->cur++; |
||
6434 |
} |
||
6435 |
TRY(test_and_skip(f, '}')); |
||
6436 |
truncate_path(f, fstate.path_len); |
||
6437 |
CALL_BACK(f, JSON_TYPE_OBJECT_END, fstate.ptr, f->cur - fstate.ptr); |
||
6438 |
} |
||
6439 |
return 0; |
||
6440 |
} |
||
6441 |
|||
6442 |
static int doit(struct frozen *f) { |
||
6443 |
if (f->cur == 0 || f->end < f->cur) return JSON_STRING_INVALID; |
||
6444 |
if (f->end == f->cur) return JSON_STRING_INCOMPLETE; |
||
6445 |
return parse_value(f); |
||
6446 |
} |
||
6447 |
|||
6448 |
int json_escape(struct json_out *out, const char *p, size_t len) WEAK; |
||
6449 |
int json_escape(struct json_out *out, const char *p, size_t len) { |
||
6450 |
size_t i, cl, n = 0; |
||
6451 |
const char *hex_digits = "0123456789abcdef"; |
||
6452 |
const char *specials = "btnvfr"; |
||
6453 |
|||
6454 |
for (i = 0; i < len; i++) { |
||
6455 |
unsigned char ch = ((unsigned char *) p)[i]; |
||
6456 |
if (ch == '"' || ch == '\\') { |
||
6457 |
n += out->printer(out, "\\", 1); |
||
6458 |
n += out->printer(out, p + i, 1); |
||
6459 |
} else if (ch >= '\b' && ch <= '\r') { |
||
6460 |
n += out->printer(out, "\\", 1); |
||
6461 |
n += out->printer(out, &specials[ch - '\b'], 1); |
||
6462 |
} else if (isprint(ch)) { |
||
6463 |
n += out->printer(out, p + i, 1); |
||
6464 |
} else if ((cl = get_utf8_char_len(ch)) == 1) { |
||
6465 |
n += out->printer(out, "\\u00", 4); |
||
6466 |
n += out->printer(out, &hex_digits[(ch >> 4) % 0xf], 1); |
||
6467 |
n += out->printer(out, &hex_digits[ch % 0xf], 1); |
||
6468 |
} else { |
||
6469 |
n += out->printer(out, p + i, cl); |
||
6470 |
i += cl - 1; |
||
6471 |
} |
||
6472 |
} |
||
6473 |
|||
6474 |
return n; |
||
6475 |
} |
||
6476 |
|||
6477 |
int json_printer_buf(struct json_out *out, const char *buf, size_t len) WEAK; |
||
6478 |
int json_printer_buf(struct json_out *out, const char *buf, size_t len) { |
||
6479 |
size_t avail = out->u.buf.size - out->u.buf.len; |
||
6480 |
size_t n = len < avail ? len : avail; |
||
6481 |
memcpy(out->u.buf.buf + out->u.buf.len, buf, n); |
||
6482 |
out->u.buf.len += n; |
||
6483 |
if (out->u.buf.size > 0) { |
||
6484 |
size_t idx = out->u.buf.len; |
||
6485 |
if (idx >= out->u.buf.size) idx = out->u.buf.size - 1; |
||
6486 |
out->u.buf.buf[idx] = '\0'; |
||
6487 |
} |
||
6488 |
return len; |
||
6489 |
} |
||
6490 |
|||
6491 |
int json_printer_file(struct json_out *out, const char *buf, size_t len) WEAK; |
||
6492 |
70 |
int json_printer_file(struct json_out *out, const char *buf, size_t len) { |
|
6493 |
70 |
return fwrite(buf, 1, len, out->u.fp); |
|
6494 |
} |
||
6495 |
|||
6496 |
static int b64idx(int c) { |
||
6497 |
if (c < 26) { |
||
6498 |
return c + 'A'; |
||
6499 |
} else if (c < 52) { |
||
6500 |
return c - 26 + 'a'; |
||
6501 |
} else if (c < 62) { |
||
6502 |
return c - 52 + '0'; |
||
6503 |
} else { |
||
6504 |
return c == 62 ? '+' : '/'; |
||
6505 |
} |
||
6506 |
} |
||
6507 |
|||
6508 |
static int b64rev(int c) { |
||
6509 |
if (c >= 'A' && c <= 'Z') { |
||
6510 |
return c - 'A'; |
||
6511 |
} else if (c >= 'a' && c <= 'z') { |
||
6512 |
return c + 26 - 'a'; |
||
6513 |
} else if (c >= '0' && c <= '9') { |
||
6514 |
return c + 52 - '0'; |
||
6515 |
} else if (c == '+') { |
||
6516 |
return 62; |
||
6517 |
} else if (c == '/') { |
||
6518 |
return 63; |
||
6519 |
} else { |
||
6520 |
return 64; |
||
6521 |
} |
||
6522 |
} |
||
6523 |
|||
6524 |
static unsigned char hexdec(const char *s) { |
||
6525 |
#define HEXTOI(x) (x >= '0' && x <= '9' ? x - '0' : x - 'W') |
||
6526 |
int a = tolower(*(const unsigned char *) s); |
||
6527 |
int b = tolower(*(const unsigned char *) (s + 1)); |
||
6528 |
return (HEXTOI(a) << 4) | HEXTOI(b); |
||
6529 |
} |
||
6530 |
|||
6531 |
static int b64enc(struct json_out *out, const unsigned char *p, int n) { |
||
6532 |
char buf[4]; |
||
6533 |
int i, len = 0; |
||
6534 |
for (i = 0; i < n; i += 3) { |
||
6535 |
int a = p[i], b = i + 1 < n ? p[i + 1] : 0, c = i + 2 < n ? p[i + 2] : 0; |
||
6536 |
buf[0] = b64idx(a >> 2); |
||
6537 |
buf[1] = b64idx((a & 3) << 4 | (b >> 4)); |
||
6538 |
buf[2] = b64idx((b & 15) << 2 | (c >> 6)); |
||
6539 |
buf[3] = b64idx(c & 63); |
||
6540 |
if (i + 1 >= n) buf[2] = '='; |
||
6541 |
if (i + 2 >= n) buf[3] = '='; |
||
6542 |
len += out->printer(out, buf, sizeof(buf)); |
||
6543 |
} |
||
6544 |
return len; |
||
6545 |
} |
||
6546 |
|||
6547 |
static int b64dec(const char *src, int n, char *dst) { |
||
6548 |
const char *end = src + n; |
||
6549 |
int len = 0; |
||
6550 |
while (src + 3 < end) { |
||
6551 |
int a = b64rev(src[0]), b = b64rev(src[1]), c = b64rev(src[2]), |
||
6552 |
d = b64rev(src[3]); |
||
6553 |
dst[len++] = (a << 2) | (b >> 4); |
||
6554 |
if (src[2] != '=') { |
||
6555 |
dst[len++] = (b << 4) | (c >> 2); |
||
6556 |
if (src[3] != '=') { |
||
6557 |
dst[len++] = (c << 6) | d; |
||
6558 |
} |
||
6559 |
} |
||
6560 |
src += 4; |
||
6561 |
} |
||
6562 |
return len; |
||
6563 |
} |
||
6564 |
|||
6565 |
int json_vprintf(struct json_out *out, const char *fmt, va_list xap) WEAK; |
||
6566 |
69 |
int json_vprintf(struct json_out *out, const char *fmt, va_list xap) { |
|
6567 |
69 |
int len = 0; |
|
6568 |
69 |
const char *quote = "\"", *null = "null"; |
|
6569 |
va_list ap; |
||
6570 |
69 |
va_copy(ap, xap); |
|
6571 |
|||
6572 |
✓✓ | 208 |
while (*fmt != '\0') { |
6573 |
✗✓ | 70 |
if (strchr(":, \r\n\t[]{}\"", *fmt) != NULL) { |
6574 |
len += out->printer(out, fmt, 1); |
||
6575 |
fmt++; |
||
6576 |
✓✗ | 70 |
} else if (fmt[0] == '%') { |
6577 |
char buf[21]; |
||
6578 |
70 |
size_t skip = 2; |
|
6579 |
|||
6580 |
✓✓✗✓ ✗✗✗✗ |
70 |
if (fmt[1] == 'l' && fmt[2] == 'l' && (fmt[3] == 'd' || fmt[3] == 'u')) { |
6581 |
int64_t val = va_arg(ap, int64_t); |
||
6582 |
const char *fmt2 = fmt[3] == 'u' ? "%" UINT64_FMT : "%" INT64_FMT; |
||
6583 |
snprintf(buf, sizeof(buf), fmt2, val); |
||
6584 |
len += out->printer(out, buf, strlen(buf)); |
||
6585 |
skip += 2; |
||
6586 |
✗✓✗✗ |
70 |
} else if (fmt[1] == 'z' && fmt[2] == 'u') { |
6587 |
size_t val = va_arg(ap, size_t); |
||
6588 |
snprintf(buf, sizeof(buf), "%lu", (unsigned long) val); |
||
6589 |
len += out->printer(out, buf, strlen(buf)); |
||
6590 |
skip += 1; |
||
6591 |
✗✓ | 70 |
} else if (fmt[1] == 'M') { |
6592 |
json_printf_callback_t f = va_arg(ap, json_printf_callback_t); |
||
6593 |
len += f(out, &ap); |
||
6594 |
✗✓ | 70 |
} else if (fmt[1] == 'B') { |
6595 |
int val = va_arg(ap, int); |
||
6596 |
const char *str = val ? "true" : "false"; |
||
6597 |
len += out->printer(out, str, strlen(str)); |
||
6598 |
✗✓ | 70 |
} else if (fmt[1] == 'H') { |
6599 |
const char *hex = "0123456789abcdef"; |
||
6600 |
int i, n = va_arg(ap, int); |
||
6601 |
const unsigned char *p = va_arg(ap, const unsigned char *); |
||
6602 |
len += out->printer(out, quote, 1); |
||
6603 |
for (i = 0; i < n; i++) { |
||
6604 |
len += out->printer(out, &hex[(p[i] >> 4) & 0xf], 1); |
||
6605 |
len += out->printer(out, &hex[p[i] & 0xf], 1); |
||
6606 |
} |
||
6607 |
len += out->printer(out, quote, 1); |
||
6608 |
✗✓ | 70 |
} else if (fmt[1] == 'V') { |
6609 |
const unsigned char *p = va_arg(ap, const unsigned char *); |
||
6610 |
int n = va_arg(ap, int); |
||
6611 |
len += out->printer(out, quote, 1); |
||
6612 |
len += b64enc(out, p, n); |
||
6613 |
len += out->printer(out, quote, 1); |
||
6614 |
✓✗✗✓ |
140 |
} else if (fmt[1] == 'Q' || |
6615 |
✗✗✗✗ |
70 |
(fmt[1] == '.' && fmt[2] == '*' && fmt[3] == 'Q')) { |
6616 |
size_t l = 0; |
||
6617 |
const char *p; |
||
6618 |
|||
6619 |
if (fmt[1] == '.') { |
||
6620 |
l = (size_t) va_arg(ap, int); |
||
6621 |
skip += 2; |
||
6622 |
} |
||
6623 |
p = va_arg(ap, char *); |
||
6624 |
|||
6625 |
if (p == NULL) { |
||
6626 |
len += out->printer(out, null, 4); |
||
6627 |
} else { |
||
6628 |
if (fmt[1] == 'Q') { |
||
6629 |
l = strlen(p); |
||
6630 |
} |
||
6631 |
len += out->printer(out, quote, 1); |
||
6632 |
len += json_escape(out, p, l); |
||
6633 |
len += out->printer(out, quote, 1); |
||
6634 |
} |
||
6635 |
} else { |
||
6636 |
/* |
||
6637 |
* we delegate printing to the system printf. |
||
6638 |
* The goal here is to delegate all modifiers parsing to the system |
||
6639 |
* printf, as you can see below we still have to parse the format |
||
6640 |
* types. |
||
6641 |
* |
||
6642 |
* Currently, %s with strings longer than 20 chars will require |
||
6643 |
* double-buffering (an auxiliary buffer will be allocated from heap). |
||
6644 |
* TODO(dfrank): reimplement %s and %.*s in order to avoid that. |
||
6645 |
*/ |
||
6646 |
|||
6647 |
70 |
const char *end_of_format_specifier = "sdfFgGlhuIcx.*-0123456789"; |
|
6648 |
70 |
int n = strspn(fmt + 1, end_of_format_specifier); |
|
6649 |
70 |
char *pbuf = buf; |
|
6650 |
70 |
int need_len, size = sizeof(buf); |
|
6651 |
char fmt2[20]; |
||
6652 |
va_list ap_copy; |
||
6653 |
✓✗ | 140 |
strncpy(fmt2, fmt, |
6654 |
140 |
n + 1 > (int) sizeof(fmt2) ? sizeof(fmt2) : (size_t) n + 1); |
|
6655 |
70 |
fmt2[n + 1] = '\0'; |
|
6656 |
|||
6657 |
70 |
va_copy(ap_copy, ap); |
|
6658 |
70 |
need_len = vsnprintf(pbuf, size, fmt2, ap_copy); |
|
6659 |
70 |
va_end(ap_copy); |
|
6660 |
|||
6661 |
✗✓ | 70 |
if (need_len < 0) { |
6662 |
/* |
||
6663 |
* Windows & eCos vsnprintf implementation return -1 on overflow |
||
6664 |
* instead of needed size. |
||
6665 |
*/ |
||
6666 |
pbuf = NULL; |
||
6667 |
while (need_len < 0) { |
||
6668 |
free(pbuf); |
||
6669 |
size *= 2; |
||
6670 |
if ((pbuf = (char *) malloc(size)) == NULL) break; |
||
6671 |
va_copy(ap_copy, ap); |
||
6672 |
need_len = vsnprintf(pbuf, size, fmt2, ap_copy); |
||
6673 |
va_end(ap_copy); |
||
6674 |
} |
||
6675 |
✗✓ | 70 |
} else if (need_len >= (int) sizeof(buf)) { |
6676 |
/* |
||
6677 |
* resulting string doesn't fit into a stack-allocated buffer `buf`, |
||
6678 |
* so we need to allocate a new buffer from heap and use it |
||
6679 |
*/ |
||
6680 |
if ((pbuf = (char *) malloc(need_len + 1)) != NULL) { |
||
6681 |
va_copy(ap_copy, ap); |
||
6682 |
vsnprintf(pbuf, need_len + 1, fmt2, ap_copy); |
||
6683 |
va_end(ap_copy); |
||
6684 |
} |
||
6685 |
} |
||
6686 |
✗✓ | 70 |
if (pbuf == NULL) { |
6687 |
buf[0] = '\0'; |
||
6688 |
pbuf = buf; |
||
6689 |
} |
||
6690 |
|||
6691 |
/* |
||
6692 |
* however we need to parse the type ourselves in order to advance |
||
6693 |
* the va_list by the correct amount; there is no portable way to |
||
6694 |
* inherit the advancement made by vprintf. |
||
6695 |
* 32-bit (linux or windows) passes va_list by value. |
||
6696 |
*/ |
||
6697 |
✓✓✗✓ ✗✓ |
70 |
if ((n + 1 == strlen("%" PRId64) && strcmp(fmt2, "%" PRId64) == 0) || |
6698 |
(n + 1 == strlen("%" PRIu64) && strcmp(fmt2, "%" PRIu64) == 0)) { |
||
6699 |
✓✗ | 1 |
(void) va_arg(ap, int64_t); |
6700 |
✗✓ | 69 |
} else if (strcmp(fmt2, "%.*s") == 0) { |
6701 |
(void) va_arg(ap, int); |
||
6702 |
(void) va_arg(ap, char *); |
||
6703 |
} else { |
||
6704 |
✗✓✗✓ |
69 |
switch (fmt2[n]) { |
6705 |
case 'u': |
||
6706 |
case 'd': |
||
6707 |
(void) va_arg(ap, int); |
||
6708 |
break; |
||
6709 |
case 'g': |
||
6710 |
case 'f': |
||
6711 |
✓✗ | 2 |
(void) va_arg(ap, double); |
6712 |
2 |
break; |
|
6713 |
case 'p': |
||
6714 |
(void) va_arg(ap, void *); |
||
6715 |
break; |
||
6716 |
default: |
||
6717 |
/* many types are promoted to int */ |
||
6718 |
✓✗ | 67 |
(void) va_arg(ap, int); |
6719 |
} |
||
6720 |
} |
||
6721 |
|||
6722 |
70 |
len += out->printer(out, pbuf, strlen(pbuf)); |
|
6723 |
70 |
skip = n + 1; |
|
6724 |
|||
6725 |
/* If buffer was allocated from heap, free it */ |
||
6726 |
✗✓ | 70 |
if (pbuf != buf) { |
6727 |
free(pbuf); |
||
6728 |
pbuf = NULL; |
||
6729 |
} |
||
6730 |
} |
||
6731 |
70 |
fmt += skip; |
|
6732 |
} else if (*fmt == '_' || is_alpha(*fmt)) { |
||
6733 |
len += out->printer(out, quote, 1); |
||
6734 |
while (*fmt == '_' || is_alpha(*fmt) || is_digit(*fmt)) { |
||
6735 |
len += out->printer(out, fmt, 1); |
||
6736 |
fmt++; |
||
6737 |
} |
||
6738 |
len += out->printer(out, quote, 1); |
||
6739 |
} else { |
||
6740 |
len += out->printer(out, fmt, 1); |
||
6741 |
fmt++; |
||
6742 |
} |
||
6743 |
} |
||
6744 |
69 |
va_end(ap); |
|
6745 |
|||
6746 |
69 |
return len; |
|
6747 |
} |
||
6748 |
|||
6749 |
int json_printf(struct json_out *out, const char *fmt, ...) WEAK; |
||
6750 |
69 |
int json_printf(struct json_out *out, const char *fmt, ...) { |
|
6751 |
int n; |
||
6752 |
va_list ap; |
||
6753 |
69 |
va_start(ap, fmt); |
|
6754 |
69 |
n = json_vprintf(out, fmt, ap); |
|
6755 |
69 |
va_end(ap); |
|
6756 |
69 |
return n; |
|
6757 |
} |
||
6758 |
|||
6759 |
int json_printf_array(struct json_out *out, va_list *ap) WEAK; |
||
6760 |
int json_printf_array(struct json_out *out, va_list *ap) { |
||
6761 |
int len = 0; |
||
6762 |
char *arr = va_arg(*ap, char *); |
||
6763 |
size_t i, arr_size = va_arg(*ap, size_t); |
||
6764 |
size_t elem_size = va_arg(*ap, size_t); |
||
6765 |
const char *fmt = va_arg(*ap, char *); |
||
6766 |
len += json_printf(out, "[", 1); |
||
6767 |
for (i = 0; arr != NULL && i < arr_size / elem_size; i++) { |
||
6768 |
union { |
||
6769 |
int64_t i; |
||
6770 |
double d; |
||
6771 |
} val; |
||
6772 |
memcpy(&val, arr + i * elem_size, |
||
6773 |
elem_size > sizeof(val) ? sizeof(val) : elem_size); |
||
6774 |
if (i > 0) len += json_printf(out, ", "); |
||
6775 |
if (strchr(fmt, 'f') != NULL) { |
||
6776 |
len += json_printf(out, fmt, val.d); |
||
6777 |
} else { |
||
6778 |
len += json_printf(out, fmt, val.i); |
||
6779 |
} |
||
6780 |
} |
||
6781 |
len += json_printf(out, "]", 1); |
||
6782 |
return len; |
||
6783 |
} |
||
6784 |
|||
6785 |
#ifdef _WIN32 |
||
6786 |
int cs_win_vsnprintf(char *str, size_t size, const char *format, |
||
6787 |
va_list ap) WEAK; |
||
6788 |
int cs_win_vsnprintf(char *str, size_t size, const char *format, va_list ap) { |
||
6789 |
int res = _vsnprintf(str, size, format, ap); |
||
6790 |
va_end(ap); |
||
6791 |
if (res >= size) { |
||
6792 |
str[size - 1] = '\0'; |
||
6793 |
} |
||
6794 |
return res; |
||
6795 |
} |
||
6796 |
|||
6797 |
int cs_win_snprintf(char *str, size_t size, const char *format, ...) WEAK; |
||
6798 |
int cs_win_snprintf(char *str, size_t size, const char *format, ...) { |
||
6799 |
int res; |
||
6800 |
va_list ap; |
||
6801 |
va_start(ap, format); |
||
6802 |
res = vsnprintf(str, size, format, ap); |
||
6803 |
va_end(ap); |
||
6804 |
return res; |
||
6805 |
} |
||
6806 |
#endif /* _WIN32 */ |
||
6807 |
|||
6808 |
int json_walk(const char *json_string, int json_string_length, |
||
6809 |
json_walk_callback_t callback, void *callback_data) WEAK; |
||
6810 |
int json_walk(const char *json_string, int json_string_length, |
||
6811 |
json_walk_callback_t callback, void *callback_data) { |
||
6812 |
struct frozen frozen; |
||
6813 |
|||
6814 |
memset(&frozen, 0, sizeof(frozen)); |
||
6815 |
frozen.end = json_string + json_string_length; |
||
6816 |
frozen.cur = json_string; |
||
6817 |
frozen.callback_data = callback_data; |
||
6818 |
frozen.callback = callback; |
||
6819 |
|||
6820 |
TRY(doit(&frozen)); |
||
6821 |
|||
6822 |
return frozen.cur - json_string; |
||
6823 |
} |
||
6824 |
|||
6825 |
struct scan_array_info { |
||
6826 |
int found; |
||
6827 |
char path[JSON_MAX_PATH_LEN]; |
||
6828 |
struct json_token *token; |
||
6829 |
}; |
||
6830 |
|||
6831 |
static void json_scanf_array_elem_cb(void *callback_data, const char *name, |
||
6832 |
size_t name_len, const char *path, |
||
6833 |
const struct json_token *token) { |
||
6834 |
struct scan_array_info *info = (struct scan_array_info *) callback_data; |
||
6835 |
|||
6836 |
(void) name; |
||
6837 |
(void) name_len; |
||
6838 |
|||
6839 |
if (strcmp(path, info->path) == 0) { |
||
6840 |
*info->token = *token; |
||
6841 |
info->found = 1; |
||
6842 |
} |
||
6843 |
} |
||
6844 |
|||
6845 |
int json_scanf_array_elem(const char *s, int len, const char *path, int idx, |
||
6846 |
struct json_token *token) WEAK; |
||
6847 |
int json_scanf_array_elem(const char *s, int len, const char *path, int idx, |
||
6848 |
struct json_token *token) { |
||
6849 |
struct scan_array_info info; |
||
6850 |
info.token = token; |
||
6851 |
info.found = 0; |
||
6852 |
memset(token, 0, sizeof(*token)); |
||
6853 |
snprintf(info.path, sizeof(info.path), "%s[%d]", path, idx); |
||
6854 |
json_walk(s, len, json_scanf_array_elem_cb, &info); |
||
6855 |
return info.found ? token->len : -1; |
||
6856 |
} |
||
6857 |
|||
6858 |
struct json_scanf_info { |
||
6859 |
int num_conversions; |
||
6860 |
char *path; |
||
6861 |
const char *fmt; |
||
6862 |
void *target; |
||
6863 |
void *user_data; |
||
6864 |
int type; |
||
6865 |
}; |
||
6866 |
|||
6867 |
int json_unescape(const char *src, int slen, char *dst, int dlen) WEAK; |
||
6868 |
int json_unescape(const char *src, int slen, char *dst, int dlen) { |
||
6869 |
char *send = (char *) src + slen, *dend = dst + dlen, *orig_dst = dst, *p; |
||
6870 |
const char *esc1 = "\"\\/bfnrt", *esc2 = "\"\\/\b\f\n\r\t"; |
||
6871 |
|||
6872 |
while (src < send) { |
||
6873 |
if (*src == '\\') { |
||
6874 |
if (++src >= send) return JSON_STRING_INCOMPLETE; |
||
6875 |
if (*src == 'u') { |
||
6876 |
/* TODO(lsm): \uXXXX escapes drag utf8 lib... Do it at some stage */ |
||
6877 |
return JSON_STRING_INVALID; |
||
6878 |
} else if ((p = (char *) strchr(esc1, *src)) != NULL) { |
||
6879 |
if (dst < dend) *dst = esc2[p - esc1]; |
||
6880 |
} else { |
||
6881 |
return JSON_STRING_INVALID; |
||
6882 |
} |
||
6883 |
} else { |
||
6884 |
if (dst < dend) *dst = *src; |
||
6885 |
} |
||
6886 |
dst++; |
||
6887 |
src++; |
||
6888 |
} |
||
6889 |
|||
6890 |
return dst - orig_dst; |
||
6891 |
} |
||
6892 |
|||
6893 |
static void json_scanf_cb(void *callback_data, const char *name, |
||
6894 |
size_t name_len, const char *path, |
||
6895 |
const struct json_token *token) { |
||
6896 |
struct json_scanf_info *info = (struct json_scanf_info *) callback_data; |
||
6897 |
char buf[32]; /* Must be enough to hold numbers */ |
||
6898 |
|||
6899 |
(void) name; |
||
6900 |
(void) name_len; |
||
6901 |
|||
6902 |
if (strcmp(path, info->path) != 0) { |
||
6903 |
/* It's not the path we're looking for, so, just ignore this callback */ |
||
6904 |
return; |
||
6905 |
} |
||
6906 |
|||
6907 |
if (token->ptr == NULL) { |
||
6908 |
/* |
||
6909 |
* We're not interested here in the events for which we have no value; |
||
6910 |
* namely, JSON_TYPE_OBJECT_START and JSON_TYPE_ARRAY_START |
||
6911 |
*/ |
||
6912 |
return; |
||
6913 |
} |
||
6914 |
|||
6915 |
switch (info->type) { |
||
6916 |
case 'B': |
||
6917 |
info->num_conversions++; |
||
6918 |
switch (sizeof(bool)) { |
||
6919 |
case sizeof(char): |
||
6920 |
*(char *) info->target = (token->type == JSON_TYPE_TRUE ? 1 : 0); |
||
6921 |
break; |
||
6922 |
case sizeof(int): |
||
6923 |
*(int *) info->target = (token->type == JSON_TYPE_TRUE ? 1 : 0); |
||
6924 |
break; |
||
6925 |
default: |
||
6926 |
/* should never be here */ |
||
6927 |
abort(); |
||
6928 |
} |
||
6929 |
break; |
||
6930 |
case 'M': { |
||
6931 |
union { |
||
6932 |
void *p; |
||
6933 |
json_scanner_t f; |
||
6934 |
} u = {info->target}; |
||
6935 |
info->num_conversions++; |
||
6936 |
u.f(token->ptr, token->len, info->user_data); |
||
6937 |
break; |
||
6938 |
} |
||
6939 |
case 'Q': { |
||
6940 |
char **dst = (char **) info->target; |
||
6941 |
if (token->type == JSON_TYPE_NULL) { |
||
6942 |
*dst = NULL; |
||
6943 |
} else { |
||
6944 |
int unescaped_len = json_unescape(token->ptr, token->len, NULL, 0); |
||
6945 |
if (unescaped_len >= 0 && |
||
6946 |
(*dst = (char *) malloc(unescaped_len + 1)) != NULL) { |
||
6947 |
info->num_conversions++; |
||
6948 |
if (json_unescape(token->ptr, token->len, *dst, unescaped_len) == |
||
6949 |
unescaped_len) { |
||
6950 |
(*dst)[unescaped_len] = '\0'; |
||
6951 |
} else { |
||
6952 |
free(*dst); |
||
6953 |
*dst = NULL; |
||
6954 |
} |
||
6955 |
} |
||
6956 |
} |
||
6957 |
break; |
||
6958 |
} |
||
6959 |
case 'H': { |
||
6960 |
char **dst = (char **) info->user_data; |
||
6961 |
int i, len = token->len / 2; |
||
6962 |
*(int *) info->target = len; |
||
6963 |
if ((*dst = (char *) malloc(len + 1)) != NULL) { |
||
6964 |
for (i = 0; i < len; i++) { |
||
6965 |
(*dst)[i] = hexdec(token->ptr + 2 * i); |
||
6966 |
} |
||
6967 |
(*dst)[len] = '\0'; |
||
6968 |
info->num_conversions++; |
||
6969 |
} |
||
6970 |
break; |
||
6971 |
} |
||
6972 |
case 'V': { |
||
6973 |
char **dst = (char **) info->target; |
||
6974 |
int len = token->len * 4 / 3 + 2; |
||
6975 |
if ((*dst = (char *) malloc(len + 1)) != NULL) { |
||
6976 |
int n = b64dec(token->ptr, token->len, *dst); |
||
6977 |
(*dst)[n] = '\0'; |
||
6978 |
*(int *) info->user_data = n; |
||
6979 |
info->num_conversions++; |
||
6980 |
} |
||
6981 |
break; |
||
6982 |
} |
||
6983 |
case 'T': |
||
6984 |
info->num_conversions++; |
||
6985 |
*(struct json_token *) info->target = *token; |
||
6986 |
break; |
||
6987 |
default: |
||
6988 |
/* Before scanf, copy into tmp buffer in order to 0-terminate it */ |
||
6989 |
if (token->len < (int) sizeof(buf)) { |
||
6990 |
memcpy(buf, token->ptr, token->len); |
||
6991 |
buf[token->len] = '\0'; |
||
6992 |
info->num_conversions += sscanf(buf, info->fmt, info->target); |
||
6993 |
} |
||
6994 |
break; |
||
6995 |
} |
||
6996 |
} |
||
6997 |
|||
6998 |
int json_vscanf(const char *s, int len, const char *fmt, va_list ap) WEAK; |
||
6999 |
int json_vscanf(const char *s, int len, const char *fmt, va_list ap) { |
||
7000 |
char path[JSON_MAX_PATH_LEN] = "", fmtbuf[20]; |
||
7001 |
int i = 0; |
||
7002 |
char *p = NULL; |
||
7003 |
struct json_scanf_info info = {0, path, fmtbuf, NULL, NULL, 0}; |
||
7004 |
|||
7005 |
while (fmt[i] != '\0') { |
||
7006 |
if (fmt[i] == '{') { |
||
7007 |
strcat(path, "."); |
||
7008 |
i++; |
||
7009 |
} else if (fmt[i] == '}') { |
||
7010 |
if ((p = strrchr(path, '.')) != NULL) *p = '\0'; |
||
7011 |
i++; |
||
7012 |
} else if (fmt[i] == '%') { |
||
7013 |
info.target = va_arg(ap, void *); |
||
7014 |
info.type = fmt[i + 1]; |
||
7015 |
switch (fmt[i + 1]) { |
||
7016 |
case 'M': |
||
7017 |
case 'V': |
||
7018 |
case 'H': |
||
7019 |
info.user_data = va_arg(ap, void *); |
||
7020 |
/* FALLTHROUGH */ |
||
7021 |
case 'B': |
||
7022 |
case 'Q': |
||
7023 |
case 'T': |
||
7024 |
i += 2; |
||
7025 |
break; |
||
7026 |
default: { |
||
7027 |
const char *delims = ", \t\r\n]}"; |
||
7028 |
int conv_len = strcspn(fmt + i + 1, delims) + 1; |
||
7029 |
snprintf(fmtbuf, sizeof(fmtbuf), "%.*s", conv_len, fmt + i); |
||
7030 |
i += conv_len; |
||
7031 |
i += strspn(fmt + i, delims); |
||
7032 |
break; |
||
7033 |
} |
||
7034 |
} |
||
7035 |
json_walk(s, len, json_scanf_cb, &info); |
||
7036 |
} else if (is_alpha(fmt[i]) || get_utf8_char_len(fmt[i]) > 1) { |
||
7037 |
const char *delims = ": \r\n\t"; |
||
7038 |
int key_len = strcspn(&fmt[i], delims); |
||
7039 |
if ((p = strrchr(path, '.')) != NULL) p[1] = '\0'; |
||
7040 |
sprintf(path + strlen(path), "%.*s", key_len, &fmt[i]); |
||
7041 |
i += key_len + strspn(fmt + i + key_len, delims); |
||
7042 |
} else { |
||
7043 |
i++; |
||
7044 |
} |
||
7045 |
} |
||
7046 |
return info.num_conversions; |
||
7047 |
} |
||
7048 |
|||
7049 |
int json_scanf(const char *str, int len, const char *fmt, ...) WEAK; |
||
7050 |
int json_scanf(const char *str, int len, const char *fmt, ...) { |
||
7051 |
int result; |
||
7052 |
va_list ap; |
||
7053 |
va_start(ap, fmt); |
||
7054 |
result = json_vscanf(str, len, fmt, ap); |
||
7055 |
va_end(ap); |
||
7056 |
return result; |
||
7057 |
} |
||
7058 |
|||
7059 |
int json_vfprintf(const char *file_name, const char *fmt, va_list ap) WEAK; |
||
7060 |
int json_vfprintf(const char *file_name, const char *fmt, va_list ap) { |
||
7061 |
int res = -1; |
||
7062 |
FILE *fp = fopen(file_name, "wb"); |
||
7063 |
if (fp != NULL) { |
||
7064 |
struct json_out out = JSON_OUT_FILE(fp); |
||
7065 |
res = json_vprintf(&out, fmt, ap); |
||
7066 |
fputc('\n', fp); |
||
7067 |
fclose(fp); |
||
7068 |
} |
||
7069 |
return res; |
||
7070 |
} |
||
7071 |
|||
7072 |
int json_fprintf(const char *file_name, const char *fmt, ...) WEAK; |
||
7073 |
int json_fprintf(const char *file_name, const char *fmt, ...) { |
||
7074 |
int result; |
||
7075 |
va_list ap; |
||
7076 |
va_start(ap, fmt); |
||
7077 |
result = json_vfprintf(file_name, fmt, ap); |
||
7078 |
va_end(ap); |
||
7079 |
return result; |
||
7080 |
} |
||
7081 |
|||
7082 |
char *json_fread(const char *path) WEAK; |
||
7083 |
char *json_fread(const char *path) { |
||
7084 |
FILE *fp; |
||
7085 |
char *data = NULL; |
||
7086 |
if ((fp = fopen(path, "rb")) == NULL) { |
||
7087 |
} else if (fseek(fp, 0, SEEK_END) != 0) { |
||
7088 |
fclose(fp); |
||
7089 |
} else { |
||
7090 |
long size = ftell(fp); |
||
7091 |
if (size > 0 && (data = (char *) malloc(size + 1)) != NULL) { |
||
7092 |
fseek(fp, 0, SEEK_SET); /* Some platforms might not have rewind(), Oo */ |
||
7093 |
if (fread(data, 1, size, fp) != (size_t) size) { |
||
7094 |
free(data); |
||
7095 |
data = NULL; |
||
7096 |
} else { |
||
7097 |
data[size] = '\0'; |
||
7098 |
} |
||
7099 |
} |
||
7100 |
fclose(fp); |
||
7101 |
} |
||
7102 |
return data; |
||
7103 |
} |
||
7104 |
|||
7105 |
struct json_setf_data { |
||
7106 |
const char *json_path; |
||
7107 |
const char *base; /* Pointer to the source JSON string */ |
||
7108 |
int matched; /* Matched part of json_path */ |
||
7109 |
int pos; /* Offset of the mutated value begin */ |
||
7110 |
int end; /* Offset of the mutated value end */ |
||
7111 |
int prev; /* Offset of the previous token end */ |
||
7112 |
}; |
||
7113 |
|||
7114 |
static int get_matched_prefix_len(const char *s1, const char *s2) { |
||
7115 |
int i = 0; |
||
7116 |
while (s1[i] && s2[i] && s1[i] == s2[i]) i++; |
||
7117 |
return i; |
||
7118 |
} |
||
7119 |
|||
7120 |
static void json_vsetf_cb(void *userdata, const char *name, size_t name_len, |
||
7121 |
const char *path, const struct json_token *t) { |
||
7122 |
struct json_setf_data *data = (struct json_setf_data *) userdata; |
||
7123 |
int off, len = get_matched_prefix_len(path, data->json_path); |
||
7124 |
if (t->ptr == NULL) return; |
||
7125 |
off = t->ptr - data->base; |
||
7126 |
// printf("--%d %s %d\n", t->type, path, off); |
||
7127 |
if (len > data->matched) data->matched = len; |
||
7128 |
|||
7129 |
/* |
||
7130 |
* If there is no exact path match, set the mutation position to tbe end |
||
7131 |
* of the object or array |
||
7132 |
*/ |
||
7133 |
if (len < data->matched && data->pos == 0 && |
||
7134 |
(t->type == JSON_TYPE_OBJECT_END || t->type == JSON_TYPE_ARRAY_END)) { |
||
7135 |
data->pos = data->end = data->prev; |
||
7136 |
} |
||
7137 |
|||
7138 |
/* Exact path match. Set mutation position to the value of this token */ |
||
7139 |
if (strcmp(path, data->json_path) == 0 && t->type != JSON_TYPE_OBJECT_START && |
||
7140 |
t->type != JSON_TYPE_ARRAY_START) { |
||
7141 |
data->pos = off; |
||
7142 |
data->end = off + t->len; |
||
7143 |
} |
||
7144 |
|||
7145 |
/* |
||
7146 |
* For deletion, we need to know where the previous value ends, because |
||
7147 |
* we don't know where matched value key starts. |
||
7148 |
* When the mutation position is not yet set, remember each value end. |
||
7149 |
* When the mutation position is already set, but it is at the beginning |
||
7150 |
* of the object/array, we catch the end of the object/array and see |
||
7151 |
* whether the object/array start is closer then previously stored prev. |
||
7152 |
*/ |
||
7153 |
if (data->pos == 0) { |
||
7154 |
data->prev = off + t->len; /* pos is not yet set */ |
||
7155 |
} else if ((t->ptr[0] == '[' || t->ptr[0] == '{') && off + 1 < data->pos && |
||
7156 |
off + 1 > data->prev) { |
||
7157 |
data->prev = off + 1; |
||
7158 |
} |
||
7159 |
(void) name; |
||
7160 |
(void) name_len; |
||
7161 |
} |
||
7162 |
|||
7163 |
int json_vsetf(const char *s, int len, struct json_out *out, |
||
7164 |
const char *json_path, const char *json_fmt, va_list ap) WEAK; |
||
7165 |
int json_vsetf(const char *s, int len, struct json_out *out, |
||
7166 |
const char *json_path, const char *json_fmt, va_list ap) { |
||
7167 |
struct json_setf_data data; |
||
7168 |
memset(&data, 0, sizeof(data)); |
||
7169 |
data.json_path = json_path; |
||
7170 |
data.base = s; |
||
7171 |
data.end = len; |
||
7172 |
// printf("S:[%.*s] %s %p\n", len, s, json_path, json_fmt); |
||
7173 |
json_walk(s, len, json_vsetf_cb, &data); |
||
7174 |
// printf("-> %d %d %d\n", data.prev, data.pos, data.end); |
||
7175 |
if (json_fmt == NULL) { |
||
7176 |
/* Deletion codepath */ |
||
7177 |
json_printf(out, "%.*s", data.prev, s); |
||
7178 |
/* Trim comma after the value that begins at object/array start */ |
||
7179 |
if (s[data.prev - 1] == '{' || s[data.prev - 1] == '[') { |
||
7180 |
int i = data.end; |
||
7181 |
while (i < len && is_space(s[i])) i++; |
||
7182 |
if (s[i] == ',') data.end = i + 1; /* Point after comma */ |
||
7183 |
} |
||
7184 |
json_printf(out, "%.*s", len - data.end, s + data.end); |
||
7185 |
} else { |
||
7186 |
/* Modification codepath */ |
||
7187 |
int n, off = data.matched, depth = 0; |
||
7188 |
|||
7189 |
/* Print the unchanged beginning */ |
||
7190 |
json_printf(out, "%.*s", data.pos, s); |
||
7191 |
|||
7192 |
/* Add missing keys */ |
||
7193 |
while ((n = strcspn(&json_path[off], ".[")) > 0) { |
||
7194 |
if (s[data.prev - 1] != '{' && s[data.prev - 1] != '[' && depth == 0) { |
||
7195 |
json_printf(out, ","); |
||
7196 |
} |
||
7197 |
if (off > 0 && json_path[off - 1] != '.') break; |
||
7198 |
json_printf(out, "%.*Q:", 1, json_path + off); |
||
7199 |
off += n; |
||
7200 |
if (json_path[off] != '\0') { |
||
7201 |
json_printf(out, "%c", json_path[off] == '.' ? '{' : '['); |
||
7202 |
depth++; |
||
7203 |
off++; |
||
7204 |
} |
||
7205 |
} |
||
7206 |
/* Print the new value */ |
||
7207 |
json_vprintf(out, json_fmt, ap); |
||
7208 |
|||
7209 |
/* Close brackets/braces of the added missing keys */ |
||
7210 |
for (; off > data.matched; off--) { |
||
7211 |
int ch = json_path[off]; |
||
7212 |
const char *p = ch == '.' ? "}" : ch == '[' ? "]" : ""; |
||
7213 |
json_printf(out, "%s", p); |
||
7214 |
} |
||
7215 |
|||
7216 |
/* Print the rest of the unchanged string */ |
||
7217 |
json_printf(out, "%.*s", len - data.end, s + data.end); |
||
7218 |
} |
||
7219 |
return data.end > data.pos ? 1 : 0; |
||
7220 |
} |
||
7221 |
|||
7222 |
int json_setf(const char *s, int len, struct json_out *out, |
||
7223 |
const char *json_path, const char *json_fmt, ...) WEAK; |
||
7224 |
int json_setf(const char *s, int len, struct json_out *out, |
||
7225 |
const char *json_path, const char *json_fmt, ...) { |
||
7226 |
int result; |
||
7227 |
va_list ap; |
||
7228 |
va_start(ap, json_fmt); |
||
7229 |
result = json_vsetf(s, len, out, json_path, json_fmt, ap); |
||
7230 |
va_end(ap); |
||
7231 |
return result; |
||
7232 |
} |
||
7233 |
|||
7234 |
struct prettify_data { |
||
7235 |
struct json_out *out; |
||
7236 |
int level; |
||
7237 |
int last_token; |
||
7238 |
}; |
||
7239 |
|||
7240 |
static void indent(struct json_out *out, int level) { |
||
7241 |
while (level-- > 0) out->printer(out, " ", 2); |
||
7242 |
} |
||
7243 |
|||
7244 |
static void print_key(struct prettify_data *pd, const char *path, |
||
7245 |
const char *name, int name_len) { |
||
7246 |
if (pd->last_token != JSON_TYPE_INVALID && |
||
7247 |
pd->last_token != JSON_TYPE_ARRAY_START && |
||
7248 |
pd->last_token != JSON_TYPE_OBJECT_START) { |
||
7249 |
pd->out->printer(pd->out, ",", 1); |
||
7250 |
} |
||
7251 |
if (path[0] != '\0') pd->out->printer(pd->out, "\n", 1); |
||
7252 |
indent(pd->out, pd->level); |
||
7253 |
if (path[0] != '\0' && path[strlen(path) - 1] != ']') { |
||
7254 |
pd->out->printer(pd->out, "\"", 1); |
||
7255 |
pd->out->printer(pd->out, name, (int) name_len); |
||
7256 |
pd->out->printer(pd->out, "\"", 1); |
||
7257 |
pd->out->printer(pd->out, ": ", 2); |
||
7258 |
} |
||
7259 |
} |
||
7260 |
|||
7261 |
static void prettify_cb(void *userdata, const char *name, size_t name_len, |
||
7262 |
const char *path, const struct json_token *t) { |
||
7263 |
struct prettify_data *pd = (struct prettify_data *) userdata; |
||
7264 |
switch (t->type) { |
||
7265 |
case JSON_TYPE_OBJECT_START: |
||
7266 |
case JSON_TYPE_ARRAY_START: |
||
7267 |
print_key(pd, path, name, name_len); |
||
7268 |
pd->out->printer(pd->out, t->type == JSON_TYPE_ARRAY_START ? "[" : "{", |
||
7269 |
1); |
||
7270 |
pd->level++; |
||
7271 |
break; |
||
7272 |
case JSON_TYPE_OBJECT_END: |
||
7273 |
case JSON_TYPE_ARRAY_END: |
||
7274 |
pd->level--; |
||
7275 |
if (pd->last_token != JSON_TYPE_INVALID && |
||
7276 |
pd->last_token != JSON_TYPE_ARRAY_START && |
||
7277 |
pd->last_token != JSON_TYPE_OBJECT_START) { |
||
7278 |
pd->out->printer(pd->out, "\n", 1); |
||
7279 |
indent(pd->out, pd->level); |
||
7280 |
} |
||
7281 |
pd->out->printer(pd->out, t->type == JSON_TYPE_ARRAY_END ? "]" : "}", 1); |
||
7282 |
break; |
||
7283 |
case JSON_TYPE_NUMBER: |
||
7284 |
case JSON_TYPE_NULL: |
||
7285 |
case JSON_TYPE_TRUE: |
||
7286 |
case JSON_TYPE_FALSE: |
||
7287 |
case JSON_TYPE_STRING: |
||
7288 |
print_key(pd, path, name, name_len); |
||
7289 |
if (t->type == JSON_TYPE_STRING) pd->out->printer(pd->out, "\"", 1); |
||
7290 |
pd->out->printer(pd->out, t->ptr, t->len); |
||
7291 |
if (t->type == JSON_TYPE_STRING) pd->out->printer(pd->out, "\"", 1); |
||
7292 |
break; |
||
7293 |
default: |
||
7294 |
break; |
||
7295 |
} |
||
7296 |
pd->last_token = t->type; |
||
7297 |
} |
||
7298 |
|||
7299 |
int json_prettify(const char *s, int len, struct json_out *out) WEAK; |
||
7300 |
int json_prettify(const char *s, int len, struct json_out *out) { |
||
7301 |
struct prettify_data pd = {out, 0, JSON_TYPE_INVALID}; |
||
7302 |
return json_walk(s, len, prettify_cb, &pd); |
||
7303 |
} |
||
7304 |
|||
7305 |
int json_prettify_file(const char *file_name) WEAK; |
||
7306 |
int json_prettify_file(const char *file_name) { |
||
7307 |
int res = -1; |
||
7308 |
char *s = json_fread(file_name); |
||
7309 |
FILE *fp; |
||
7310 |
if (s != NULL && (fp = fopen(file_name, "wb")) != NULL) { |
||
7311 |
struct json_out out = JSON_OUT_FILE(fp); |
||
7312 |
res = json_prettify(s, strlen(s), &out); |
||
7313 |
if (res < 0) { |
||
7314 |
/* On error, restore the old content */ |
||
7315 |
fclose(fp); |
||
7316 |
fp = fopen(file_name, "wb"); |
||
7317 |
fseek(fp, 0, SEEK_SET); |
||
7318 |
fwrite(s, 1, strlen(s), fp); |
||
7319 |
} else { |
||
7320 |
fputc('\n', fp); |
||
7321 |
} |
||
7322 |
fclose(fp); |
||
7323 |
} |
||
7324 |
free(s); |
||
7325 |
return res; |
||
7326 |
} |
||
7327 |
|||
7328 |
struct next_data { |
||
7329 |
void *handle; // Passed handle. Changed if a next entry is found |
||
7330 |
const char *path; // Path to the iterated object/array |
||
7331 |
int path_len; // Path length - optimisation |
||
7332 |
int found; // Non-0 if found the next entry |
||
7333 |
struct json_token *key; // Object's key |
||
7334 |
struct json_token *val; // Object's value |
||
7335 |
int *idx; // Array index |
||
7336 |
}; |
||
7337 |
|||
7338 |
static void next_set_key(struct next_data *d, const char *name, int name_len, |
||
7339 |
int is_array) { |
||
7340 |
if (is_array) { |
||
7341 |
/* Array. Set index and reset key */ |
||
7342 |
if (d->key != NULL) { |
||
7343 |
d->key->len = 0; |
||
7344 |
d->key->ptr = NULL; |
||
7345 |
} |
||
7346 |
if (d->idx != NULL) *d->idx = atoi(name); |
||
7347 |
} else { |
||
7348 |
/* Object. Set key and make index -1 */ |
||
7349 |
if (d->key != NULL) { |
||
7350 |
d->key->ptr = name; |
||
7351 |
d->key->len = name_len; |
||
7352 |
} |
||
7353 |
if (d->idx != NULL) *d->idx = -1; |
||
7354 |
} |
||
7355 |
} |
||
7356 |
|||
7357 |
static void next_cb(void *userdata, const char *name, size_t name_len, |
||
7358 |
const char *path, const struct json_token *t) { |
||
7359 |
struct next_data *d = (struct next_data *) userdata; |
||
7360 |
const char *p = path + d->path_len; |
||
7361 |
if (d->found) return; |
||
7362 |
if (d->path_len >= (int) strlen(path)) return; |
||
7363 |
if (strncmp(d->path, path, d->path_len) != 0) return; |
||
7364 |
if (strchr(p + 1, '.') != NULL) return; /* More nested objects - skip */ |
||
7365 |
if (strchr(p + 1, '[') != NULL) return; /* Ditto for arrays */ |
||
7366 |
// {OBJECT,ARRAY}_END types do not pass name, _START does. Save key. |
||
7367 |
if (t->type == JSON_TYPE_OBJECT_START || t->type == JSON_TYPE_ARRAY_START) { |
||
7368 |
// printf("SAV %s %d %p\n", path, t->type, t->ptr); |
||
7369 |
next_set_key(d, name, name_len, p[0] == '['); |
||
7370 |
} else if (d->handle == NULL || d->handle < (void *) t->ptr) { |
||
7371 |
// printf("END %s %d %p\n", path, t->type, t->ptr); |
||
7372 |
if (t->type != JSON_TYPE_OBJECT_END && t->type != JSON_TYPE_ARRAY_END) { |
||
7373 |
next_set_key(d, name, name_len, p[0] == '['); |
||
7374 |
} |
||
7375 |
if (d->val != NULL) *d->val = *t; |
||
7376 |
d->handle = (void *) t->ptr; |
||
7377 |
d->found = 1; |
||
7378 |
} |
||
7379 |
} |
||
7380 |
|||
7381 |
static void *json_next(const char *s, int len, void *handle, const char *path, |
||
7382 |
struct json_token *key, struct json_token *val, int *i) { |
||
7383 |
struct json_token tmpval, *v = val == NULL ? &tmpval : val; |
||
7384 |
struct json_token tmpkey, *k = key == NULL ? &tmpkey : key; |
||
7385 |
int tmpidx, *pidx = i == NULL ? &tmpidx : i; |
||
7386 |
struct next_data data = {handle, path, strlen(path), 0, k, v, pidx}; |
||
7387 |
json_walk(s, len, next_cb, &data); |
||
7388 |
return data.found ? data.handle : NULL; |
||
7389 |
} |
||
7390 |
|||
7391 |
void *json_next_key(const char *s, int len, void *handle, const char *path, |
||
7392 |
struct json_token *key, struct json_token *val) WEAK; |
||
7393 |
void *json_next_key(const char *s, int len, void *handle, const char *path, |
||
7394 |
struct json_token *key, struct json_token *val) { |
||
7395 |
return json_next(s, len, handle, path, key, val, NULL); |
||
7396 |
} |
||
7397 |
|||
7398 |
void *json_next_elem(const char *s, int len, void *handle, const char *path, |
||
7399 |
int *idx, struct json_token *val) WEAK; |
||
7400 |
void *json_next_elem(const char *s, int len, void *handle, const char *path, |
||
7401 |
int *idx, struct json_token *val) { |
||
7402 |
return json_next(s, len, handle, path, NULL, val, idx); |
||
7403 |
} |
||
7404 |
|||
7405 |
static int json_sprinter(struct json_out *out, const char *str, size_t len) { |
||
7406 |
size_t old_len = out->u.buf.buf == NULL ? 0 : strlen(out->u.buf.buf); |
||
7407 |
size_t new_len = len + old_len; |
||
7408 |
char *p = (char *) realloc(out->u.buf.buf, new_len + 1); |
||
7409 |
if (p != NULL) { |
||
7410 |
memcpy(p + old_len, str, len); |
||
7411 |
p[new_len] = '\0'; |
||
7412 |
out->u.buf.buf = p; |
||
7413 |
} |
||
7414 |
return len; |
||
7415 |
} |
||
7416 |
|||
7417 |
char *json_vasprintf(const char *fmt, va_list ap) WEAK; |
||
7418 |
char *json_vasprintf(const char *fmt, va_list ap) { |
||
7419 |
struct json_out out; |
||
7420 |
memset(&out, 0, sizeof(out)); |
||
7421 |
out.printer = json_sprinter; |
||
7422 |
json_vprintf(&out, fmt, ap); |
||
7423 |
return out.u.buf.buf; |
||
7424 |
} |
||
7425 |
|||
7426 |
char *json_asprintf(const char *fmt, ...) WEAK; |
||
7427 |
char *json_asprintf(const char *fmt, ...) { |
||
7428 |
char *result = NULL; |
||
7429 |
va_list ap; |
||
7430 |
va_start(ap, fmt); |
||
7431 |
result = json_vasprintf(fmt, ap); |
||
7432 |
va_end(ap); |
||
7433 |
return result; |
||
7434 |
} |
||
7435 |
#ifdef MJS_MODULE_LINES |
||
7436 |
#line 1 "mjs/src/ffi/ffi.c" |
||
7437 |
#endif |
||
7438 |
/* |
||
7439 |
* Copyright (c) 2016 Cesanta Software Limited |
||
7440 |
* All rights reserved |
||
7441 |
*/ |
||
7442 |
|||
7443 |
/* Amalgamated: #include "mjs/src/ffi/ffi.h" */ |
||
7444 |
|||
7445 |
#define IS_W(arg) ((arg).ctype == FFI_CTYPE_WORD) |
||
7446 |
#define IS_D(arg) ((arg).ctype == FFI_CTYPE_DOUBLE) |
||
7447 |
#define IS_F(arg) ((arg).ctype == FFI_CTYPE_FLOAT) |
||
7448 |
|||
7449 |
#define W(arg) ((ffi_word_t)(arg).v.i) |
||
7450 |
#define D(arg) ((arg).v.d) |
||
7451 |
#define F(arg) ((arg).v.f) |
||
7452 |
|||
7453 |
void ffi_set_word(struct ffi_arg *arg, ffi_word_t v) { |
||
7454 |
arg->ctype = FFI_CTYPE_WORD; |
||
7455 |
arg->v.i = v; |
||
7456 |
} |
||
7457 |
|||
7458 |
void ffi_set_bool(struct ffi_arg *arg, bool v) { |
||
7459 |
arg->ctype = FFI_CTYPE_BOOL; |
||
7460 |
arg->v.i = v; |
||
7461 |
} |
||
7462 |
|||
7463 |
void ffi_set_ptr(struct ffi_arg *arg, void *v) { |
||
7464 |
ffi_set_word(arg, (ffi_word_t) v); |
||
7465 |
} |
||
7466 |
|||
7467 |
void ffi_set_double(struct ffi_arg *arg, double v) { |
||
7468 |
arg->ctype = FFI_CTYPE_DOUBLE; |
||
7469 |
arg->v.d = v; |
||
7470 |
} |
||
7471 |
|||
7472 |
void ffi_set_float(struct ffi_arg *arg, float v) { |
||
7473 |
arg->ctype = FFI_CTYPE_FLOAT; |
||
7474 |
arg->v.f = v; |
||
7475 |
} |
||
7476 |
|||
7477 |
/* |
||
7478 |
* The ARM ABI uses only 4 32-bit registers for paramter passing. |
||
7479 |
* Xtensa call0 calling-convention (as used by Espressif) has 6. |
||
7480 |
* |
||
7481 |
* Focusing only on implementing FFI with registers means we can simplify a lot. |
||
7482 |
* |
||
7483 |
* ARM has some quasi-alignment rules when mixing double and integers as |
||
7484 |
* arguments. Only: |
||
7485 |
* a) double, int32_t, int32_t |
||
7486 |
* b) int32_t, double |
||
7487 |
* would fit in 4 registers. (the same goes for uint64_t). |
||
7488 |
* |
||
7489 |
* In order to simplify further, when a double-width argument is present, we |
||
7490 |
* allow only two arguments. |
||
7491 |
*/ |
||
7492 |
|||
7493 |
/* |
||
7494 |
* We need to support x86_64 in order to support local tests. |
||
7495 |
* x86_64 has more and wider registers, but unlike the two main |
||
7496 |
* embedded platforms we target it has a separate register file for |
||
7497 |
* integer values and for floating point values (both for passing args and |
||
7498 |
* return values). E.g. if a double value is passed as a second argument |
||
7499 |
* it gets passed in the first available floating point register. |
||
7500 |
* |
||
7501 |
* I.e, the compiler generates exactly the same code for: |
||
7502 |
* |
||
7503 |
* void foo(int a, double b) {...} |
||
7504 |
* |
||
7505 |
* and |
||
7506 |
* |
||
7507 |
* void foo(double b, int a) {...} |
||
7508 |
* |
||
7509 |
* |
||
7510 |
*/ |
||
7511 |
|||
7512 |
typedef ffi_word_t (*w4w_t)(ffi_word_t, ffi_word_t, ffi_word_t, ffi_word_t); |
||
7513 |
typedef ffi_word_t (*w5w_t)(ffi_word_t, ffi_word_t, ffi_word_t, ffi_word_t, |
||
7514 |
ffi_word_t); |
||
7515 |
typedef ffi_word_t (*w6w_t)(ffi_word_t, ffi_word_t, ffi_word_t, ffi_word_t, |
||
7516 |
ffi_word_t, ffi_word_t); |
||
7517 |
|||
7518 |
typedef ffi_word_t (*wdw_t)(double, ffi_word_t); |
||
7519 |
typedef ffi_word_t (*wwd_t)(ffi_word_t, double); |
||
7520 |
typedef ffi_word_t (*wdd_t)(double, double); |
||
7521 |
|||
7522 |
typedef ffi_word_t (*wwwd_t)(ffi_word_t, ffi_word_t, double); |
||
7523 |
typedef ffi_word_t (*wwdw_t)(ffi_word_t, double, ffi_word_t); |
||
7524 |
typedef ffi_word_t (*wwdd_t)(ffi_word_t, double, double); |
||
7525 |
typedef ffi_word_t (*wdww_t)(double, ffi_word_t, ffi_word_t); |
||
7526 |
typedef ffi_word_t (*wdwd_t)(double, ffi_word_t, double); |
||
7527 |
typedef ffi_word_t (*wddw_t)(double, double, ffi_word_t); |
||
7528 |
typedef ffi_word_t (*wddd_t)(double, double, double); |
||
7529 |
|||
7530 |
typedef ffi_word_t (*wfw_t)(float, ffi_word_t); |
||
7531 |
typedef ffi_word_t (*wwf_t)(ffi_word_t, float); |
||
7532 |
typedef ffi_word_t (*wff_t)(float, float); |
||
7533 |
|||
7534 |
typedef ffi_word_t (*wwwf_t)(ffi_word_t, ffi_word_t, float); |
||
7535 |
typedef ffi_word_t (*wwfw_t)(ffi_word_t, float, ffi_word_t); |
||
7536 |
typedef ffi_word_t (*wwff_t)(ffi_word_t, float, float); |
||
7537 |
typedef ffi_word_t (*wfww_t)(float, ffi_word_t, ffi_word_t); |
||
7538 |
typedef ffi_word_t (*wfwf_t)(float, ffi_word_t, float); |
||
7539 |
typedef ffi_word_t (*wffw_t)(float, float, ffi_word_t); |
||
7540 |
typedef ffi_word_t (*wfff_t)(float, float, float); |
||
7541 |
|||
7542 |
typedef bool (*b4w_t)(ffi_word_t, ffi_word_t, ffi_word_t, ffi_word_t); |
||
7543 |
typedef bool (*b5w_t)(ffi_word_t, ffi_word_t, ffi_word_t, ffi_word_t, |
||
7544 |
ffi_word_t); |
||
7545 |
typedef bool (*b6w_t)(ffi_word_t, ffi_word_t, ffi_word_t, ffi_word_t, |
||
7546 |
ffi_word_t, ffi_word_t); |
||
7547 |
typedef bool (*bdw_t)(double, ffi_word_t); |
||
7548 |
typedef bool (*bwd_t)(ffi_word_t, double); |
||
7549 |
typedef bool (*bdd_t)(double, double); |
||
7550 |
|||
7551 |
typedef bool (*bwwd_t)(ffi_word_t, ffi_word_t, double); |
||
7552 |
typedef bool (*bwdw_t)(ffi_word_t, double, ffi_word_t); |
||
7553 |
typedef bool (*bwdd_t)(ffi_word_t, double, double); |
||
7554 |
typedef bool (*bdww_t)(double, ffi_word_t, ffi_word_t); |
||
7555 |
typedef bool (*bdwd_t)(double, ffi_word_t, double); |
||
7556 |
typedef bool (*bddw_t)(double, double, ffi_word_t); |
||
7557 |
typedef bool (*bddd_t)(double, double, double); |
||
7558 |
|||
7559 |
typedef bool (*bfw_t)(float, ffi_word_t); |
||
7560 |
typedef bool (*bwf_t)(ffi_word_t, float); |
||
7561 |
typedef bool (*bff_t)(float, float); |
||
7562 |
|||
7563 |
typedef bool (*bwwf_t)(ffi_word_t, ffi_word_t, float); |
||
7564 |
typedef bool (*bwfw_t)(ffi_word_t, float, ffi_word_t); |
||
7565 |
typedef bool (*bwff_t)(ffi_word_t, float, float); |
||
7566 |
typedef bool (*bfww_t)(float, ffi_word_t, ffi_word_t); |
||
7567 |
typedef bool (*bfwf_t)(float, ffi_word_t, float); |
||
7568 |
typedef bool (*bffw_t)(float, float, ffi_word_t); |
||
7569 |
typedef bool (*bfff_t)(float, float, float); |
||
7570 |
|||
7571 |
typedef double (*d4w_t)(ffi_word_t, ffi_word_t, ffi_word_t, ffi_word_t); |
||
7572 |
typedef double (*d5w_t)(ffi_word_t, ffi_word_t, ffi_word_t, ffi_word_t, |
||
7573 |
ffi_word_t); |
||
7574 |
typedef double (*d6w_t)(ffi_word_t, ffi_word_t, ffi_word_t, ffi_word_t, |
||
7575 |
ffi_word_t, ffi_word_t); |
||
7576 |
typedef double (*ddw_t)(double, ffi_word_t); |
||
7577 |
typedef double (*dwd_t)(ffi_word_t, double); |
||
7578 |
typedef double (*ddd_t)(double, double); |
||
7579 |
|||
7580 |
typedef double (*dwwd_t)(ffi_word_t, ffi_word_t, double); |
||
7581 |
typedef double (*dwdw_t)(ffi_word_t, double, ffi_word_t); |
||
7582 |
typedef double (*dwdd_t)(ffi_word_t, double, double); |
||
7583 |
typedef double (*ddww_t)(double, ffi_word_t, ffi_word_t); |
||
7584 |
typedef double (*ddwd_t)(double, ffi_word_t, double); |
||
7585 |
typedef double (*dddw_t)(double, double, ffi_word_t); |
||
7586 |
typedef double (*dddd_t)(double, double, double); |
||
7587 |
|||
7588 |
typedef float (*f4w_t)(ffi_word_t, ffi_word_t, ffi_word_t, ffi_word_t); |
||
7589 |
typedef float (*f5w_t)(ffi_word_t, ffi_word_t, ffi_word_t, ffi_word_t, |
||
7590 |
ffi_word_t); |
||
7591 |
typedef float (*f6w_t)(ffi_word_t, ffi_word_t, ffi_word_t, ffi_word_t, |
||
7592 |
ffi_word_t, ffi_word_t); |
||
7593 |
typedef float (*ffw_t)(float, ffi_word_t); |
||
7594 |
typedef float (*fwf_t)(ffi_word_t, float); |
||
7595 |
typedef float (*fff_t)(float, float); |
||
7596 |
|||
7597 |
typedef float (*fwwf_t)(ffi_word_t, ffi_word_t, float); |
||
7598 |
typedef float (*fwfw_t)(ffi_word_t, float, ffi_word_t); |
||
7599 |
typedef float (*fwff_t)(ffi_word_t, float, float); |
||
7600 |
typedef float (*ffww_t)(float, ffi_word_t, ffi_word_t); |
||
7601 |
typedef float (*ffwf_t)(float, ffi_word_t, float); |
||
7602 |
typedef float (*fffw_t)(float, float, ffi_word_t); |
||
7603 |
typedef float (*ffff_t)(float, float, float); |
||
7604 |
|||
7605 |
int ffi_call(ffi_fn_t *func, int nargs, struct ffi_arg *res, |
||
7606 |
struct ffi_arg *args) { |
||
7607 |
int i, doubles = 0, floats = 0; |
||
7608 |
|||
7609 |
if (nargs > 6) return -1; |
||
7610 |
for (i = 0; i < nargs; i++) { |
||
7611 |
doubles += (IS_D(args[i])); |
||
7612 |
floats += (IS_F(args[i])); |
||
7613 |
} |
||
7614 |
|||
7615 |
/* Doubles and floats are not supported together atm */ |
||
7616 |
if (doubles > 0 && floats > 0) { |
||
7617 |
return -1; |
||
7618 |
} |
||
7619 |
|||
7620 |
switch (res->ctype) { |
||
7621 |
case FFI_CTYPE_WORD: { /* {{{ */ |
||
7622 |
ffi_word_t r; |
||
7623 |
if (doubles == 0) { |
||
7624 |
if (floats == 0) { |
||
7625 |
/* |
||
7626 |
* No double and no float args: we currently support up to 6 |
||
7627 |
* word-sized arguments |
||
7628 |
*/ |
||
7629 |
if (nargs <= 4) { |
||
7630 |
w4w_t f = (w4w_t) func; |
||
7631 |
r = f(W(args[0]), W(args[1]), W(args[2]), W(args[3])); |
||
7632 |
} else if (nargs == 5) { |
||
7633 |
w5w_t f = (w5w_t) func; |
||
7634 |
r = f(W(args[0]), W(args[1]), W(args[2]), W(args[3]), W(args[4])); |
||
7635 |
} else if (nargs == 6) { |
||
7636 |
w6w_t f = (w6w_t) func; |
||
7637 |
r = f(W(args[0]), W(args[1]), W(args[2]), W(args[3]), W(args[4]), |
||
7638 |
W(args[5])); |
||
7639 |
} else { |
||
7640 |
abort(); |
||
7641 |
} |
||
7642 |
} else { |
||
7643 |
/* There are some floats */ |
||
7644 |
switch (nargs) { |
||
7645 |
case 0: |
||
7646 |
case 1: |
||
7647 |
case 2: |
||
7648 |
if (IS_F(args[0]) && IS_F(args[1])) { |
||
7649 |
wff_t f = (wff_t) func; |
||
7650 |
r = f(F(args[0]), F(args[1])); |
||
7651 |
} else if (IS_F(args[0])) { |
||
7652 |
wfw_t f = (wfw_t) func; |
||
7653 |
r = f(F(args[0]), W(args[1])); |
||
7654 |
} else { |
||
7655 |
wwf_t f = (wwf_t) func; |
||
7656 |
r = f(W(args[0]), F(args[1])); |
||
7657 |
} |
||
7658 |
break; |
||
7659 |
|||
7660 |
case 3: |
||
7661 |
if (IS_W(args[0]) && IS_W(args[1]) && IS_F(args[2])) { |
||
7662 |
wwwf_t f = (wwwf_t) func; |
||
7663 |
r = f(W(args[0]), W(args[1]), F(args[2])); |
||
7664 |
} else if (IS_W(args[0]) && IS_F(args[1]) && IS_W(args[2])) { |
||
7665 |
wwfw_t f = (wwfw_t) func; |
||
7666 |
r = f(W(args[0]), F(args[1]), W(args[2])); |
||
7667 |
} else if (IS_W(args[0]) && IS_F(args[1]) && IS_F(args[2])) { |
||
7668 |
wwff_t f = (wwff_t) func; |
||
7669 |
r = f(W(args[0]), F(args[1]), F(args[2])); |
||
7670 |
} else if (IS_F(args[0]) && IS_W(args[1]) && IS_W(args[2])) { |
||
7671 |
wfww_t f = (wfww_t) func; |
||
7672 |
r = f(F(args[0]), W(args[1]), W(args[2])); |
||
7673 |
} else if (IS_F(args[0]) && IS_W(args[1]) && IS_F(args[2])) { |
||
7674 |
wfwf_t f = (wfwf_t) func; |
||
7675 |
r = f(F(args[0]), W(args[1]), F(args[2])); |
||
7676 |
} else if (IS_F(args[0]) && IS_F(args[1]) && IS_W(args[2])) { |
||
7677 |
wffw_t f = (wffw_t) func; |
||
7678 |
r = f(F(args[0]), F(args[1]), W(args[2])); |
||
7679 |
} else if (IS_F(args[0]) && IS_F(args[1]) && IS_F(args[2])) { |
||
7680 |
wfff_t f = (wfff_t) func; |
||
7681 |
r = f(F(args[0]), F(args[1]), F(args[2])); |
||
7682 |
} else { |
||
7683 |
// The above checks should be exhaustive |
||
7684 |
abort(); |
||
7685 |
} |
||
7686 |
break; |
||
7687 |
default: |
||
7688 |
return -1; |
||
7689 |
} |
||
7690 |
} |
||
7691 |
} else { |
||
7692 |
/* There are some doubles */ |
||
7693 |
switch (nargs) { |
||
7694 |
case 0: |
||
7695 |
case 1: |
||
7696 |
case 2: |
||
7697 |
if (IS_D(args[0]) && IS_D(args[1])) { |
||
7698 |
wdd_t f = (wdd_t) func; |
||
7699 |
r = f(D(args[0]), D(args[1])); |
||
7700 |
} else if (IS_D(args[0])) { |
||
7701 |
wdw_t f = (wdw_t) func; |
||
7702 |
r = f(D(args[0]), W(args[1])); |
||
7703 |
} else { |
||
7704 |
wwd_t f = (wwd_t) func; |
||
7705 |
r = f(W(args[0]), D(args[1])); |
||
7706 |
} |
||
7707 |
break; |
||
7708 |
|||
7709 |
case 3: |
||
7710 |
if (IS_W(args[0]) && IS_W(args[1]) && IS_D(args[2])) { |
||
7711 |
wwwd_t f = (wwwd_t) func; |
||
7712 |
r = f(W(args[0]), W(args[1]), D(args[2])); |
||
7713 |
} else if (IS_W(args[0]) && IS_D(args[1]) && IS_W(args[2])) { |
||
7714 |
wwdw_t f = (wwdw_t) func; |
||
7715 |
r = f(W(args[0]), D(args[1]), W(args[2])); |
||
7716 |
} else if (IS_W(args[0]) && IS_D(args[1]) && IS_D(args[2])) { |
||
7717 |
wwdd_t f = (wwdd_t) func; |
||
7718 |
r = f(W(args[0]), D(args[1]), D(args[2])); |
||
7719 |
} else if (IS_D(args[0]) && IS_W(args[1]) && IS_W(args[2])) { |
||
7720 |
wdww_t f = (wdww_t) func; |
||
7721 |
r = f(D(args[0]), W(args[1]), W(args[2])); |
||
7722 |
} else if (IS_D(args[0]) && IS_W(args[1]) && IS_D(args[2])) { |
||
7723 |
wdwd_t f = (wdwd_t) func; |
||
7724 |
r = f(D(args[0]), W(args[1]), D(args[2])); |
||
7725 |
} else if (IS_D(args[0]) && IS_D(args[1]) && IS_W(args[2])) { |
||
7726 |
wddw_t f = (wddw_t) func; |
||
7727 |
r = f(D(args[0]), D(args[1]), W(args[2])); |
||
7728 |
} else if (IS_D(args[0]) && IS_D(args[1]) && IS_D(args[2])) { |
||
7729 |
wddd_t f = (wddd_t) func; |
||
7730 |
r = f(D(args[0]), D(args[1]), D(args[2])); |
||
7731 |
} else { |
||
7732 |
// The above checks should be exhaustive |
||
7733 |
abort(); |
||
7734 |
} |
||
7735 |
break; |
||
7736 |
default: |
||
7737 |
return -1; |
||
7738 |
} |
||
7739 |
} |
||
7740 |
res->v.i = (uint64_t) r; |
||
7741 |
} break; /* }}} */ |
||
7742 |
case FFI_CTYPE_BOOL: { /* {{{ */ |
||
7743 |
ffi_word_t r; |
||
7744 |
if (doubles == 0) { |
||
7745 |
if (floats == 0) { |
||
7746 |
/* |
||
7747 |
* No double and no float args: we currently support up to 6 |
||
7748 |
* word-sized arguments |
||
7749 |
*/ |
||
7750 |
if (nargs <= 4) { |
||
7751 |
b4w_t f = (b4w_t) func; |
||
7752 |
r = f(W(args[0]), W(args[1]), W(args[2]), W(args[3])); |
||
7753 |
} else if (nargs == 5) { |
||
7754 |
b5w_t f = (b5w_t) func; |
||
7755 |
r = f(W(args[0]), W(args[1]), W(args[2]), W(args[3]), W(args[4])); |
||
7756 |
} else if (nargs == 6) { |
||
7757 |
b6w_t f = (b6w_t) func; |
||
7758 |
r = f(W(args[0]), W(args[1]), W(args[2]), W(args[3]), W(args[4]), |
||
7759 |
W(args[5])); |
||
7760 |
} else { |
||
7761 |
abort(); |
||
7762 |
} |
||
7763 |
} else { |
||
7764 |
/* There are some floats */ |
||
7765 |
switch (nargs) { |
||
7766 |
case 0: |
||
7767 |
case 1: |
||
7768 |
case 2: |
||
7769 |
if (IS_F(args[0]) && IS_F(args[1])) { |
||
7770 |
bff_t f = (bff_t) func; |
||
7771 |
r = f(F(args[0]), F(args[1])); |
||
7772 |
} else if (IS_F(args[0])) { |
||
7773 |
bfw_t f = (bfw_t) func; |
||
7774 |
r = f(F(args[0]), W(args[1])); |
||
7775 |
} else { |
||
7776 |
bwf_t f = (bwf_t) func; |
||
7777 |
r = f(W(args[0]), F(args[1])); |
||
7778 |
} |
||
7779 |
break; |
||
7780 |
|||
7781 |
case 3: |
||
7782 |
if (IS_W(args[0]) && IS_W(args[1]) && IS_F(args[2])) { |
||
7783 |
bwwf_t f = (bwwf_t) func; |
||
7784 |
r = f(W(args[0]), W(args[1]), F(args[2])); |
||
7785 |
} else if (IS_W(args[0]) && IS_F(args[1]) && IS_W(args[2])) { |
||
7786 |
bwfw_t f = (bwfw_t) func; |
||
7787 |
r = f(W(args[0]), F(args[1]), W(args[2])); |
||
7788 |
} else if (IS_W(args[0]) && IS_F(args[1]) && IS_F(args[2])) { |
||
7789 |
bwff_t f = (bwff_t) func; |
||
7790 |
r = f(W(args[0]), F(args[1]), F(args[2])); |
||
7791 |
} else if (IS_F(args[0]) && IS_W(args[1]) && IS_W(args[2])) { |
||
7792 |
bfww_t f = (bfww_t) func; |
||
7793 |
r = f(F(args[0]), W(args[1]), W(args[2])); |
||
7794 |
} else if (IS_F(args[0]) && IS_W(args[1]) && IS_F(args[2])) { |
||
7795 |
bfwf_t f = (bfwf_t) func; |
||
7796 |
r = f(F(args[0]), W(args[1]), F(args[2])); |
||
7797 |
} else if (IS_F(args[0]) && IS_F(args[1]) && IS_W(args[2])) { |
||
7798 |
bffw_t f = (bffw_t) func; |
||
7799 |
r = f(F(args[0]), F(args[1]), W(args[2])); |
||
7800 |
} else if (IS_F(args[0]) && IS_F(args[1]) && IS_F(args[2])) { |
||
7801 |
bfff_t f = (bfff_t) func; |
||
7802 |
r = f(F(args[0]), F(args[1]), F(args[2])); |
||
7803 |
} else { |
||
7804 |
// The above checks should be exhaustive |
||
7805 |
abort(); |
||
7806 |
} |
||
7807 |
break; |
||
7808 |
default: |
||
7809 |
return -1; |
||
7810 |
} |
||
7811 |
} |
||
7812 |
} else { |
||
7813 |
/* There are some doubles */ |
||
7814 |
switch (nargs) { |
||
7815 |
case 0: |
||
7816 |
case 1: |
||
7817 |
case 2: |
||
7818 |
if (IS_D(args[0]) && IS_D(args[1])) { |
||
7819 |
bdd_t f = (bdd_t) func; |
||
7820 |
r = f(D(args[0]), D(args[1])); |
||
7821 |
} else if (IS_D(args[0])) { |
||
7822 |
bdw_t f = (bdw_t) func; |
||
7823 |
r = f(D(args[0]), W(args[1])); |
||
7824 |
} else { |
||
7825 |
bwd_t f = (bwd_t) func; |
||
7826 |
r = f(W(args[0]), D(args[1])); |
||
7827 |
} |
||
7828 |
break; |
||
7829 |
|||
7830 |
case 3: |
||
7831 |
if (IS_W(args[0]) && IS_W(args[1]) && IS_D(args[2])) { |
||
7832 |
bwwd_t f = (bwwd_t) func; |
||
7833 |
r = f(W(args[0]), W(args[1]), D(args[2])); |
||
7834 |
} else if (IS_W(args[0]) && IS_D(args[1]) && IS_W(args[2])) { |
||
7835 |
bwdw_t f = (bwdw_t) func; |
||
7836 |
r = f(W(args[0]), D(args[1]), W(args[2])); |
||
7837 |
} else if (IS_W(args[0]) && IS_D(args[1]) && IS_D(args[2])) { |
||
7838 |
bwdd_t f = (bwdd_t) func; |
||
7839 |
r = f(W(args[0]), D(args[1]), D(args[2])); |
||
7840 |
} else if (IS_D(args[0]) && IS_W(args[1]) && IS_W(args[2])) { |
||
7841 |
bdww_t f = (bdww_t) func; |
||
7842 |
r = f(D(args[0]), W(args[1]), W(args[2])); |
||
7843 |
} else if (IS_D(args[0]) && IS_W(args[1]) && IS_D(args[2])) { |
||
7844 |
bdwd_t f = (bdwd_t) func; |
||
7845 |
r = f(D(args[0]), W(args[1]), D(args[2])); |
||
7846 |
} else if (IS_D(args[0]) && IS_D(args[1]) && IS_W(args[2])) { |
||
7847 |
bddw_t f = (bddw_t) func; |
||
7848 |
r = f(D(args[0]), D(args[1]), W(args[2])); |
||
7849 |
} else if (IS_D(args[0]) && IS_D(args[1]) && IS_D(args[2])) { |
||
7850 |
bddd_t f = (bddd_t) func; |
||
7851 |
r = f(D(args[0]), D(args[1]), D(args[2])); |
||
7852 |
} else { |
||
7853 |
// The above checks should be exhaustive |
||
7854 |
abort(); |
||
7855 |
} |
||
7856 |
break; |
||
7857 |
default: |
||
7858 |
return -1; |
||
7859 |
} |
||
7860 |
} |
||
7861 |
res->v.i = (uint64_t) r; |
||
7862 |
} break; /* }}} */ |
||
7863 |
case FFI_CTYPE_DOUBLE: { /* {{{ */ |
||
7864 |
double r; |
||
7865 |
if (doubles == 0) { |
||
7866 |
/* No double args: we currently support up to 6 word-sized arguments |
||
7867 |
*/ |
||
7868 |
if (nargs <= 4) { |
||
7869 |
d4w_t f = (d4w_t) func; |
||
7870 |
r = f(W(args[0]), W(args[1]), W(args[2]), W(args[3])); |
||
7871 |
} else if (nargs == 5) { |
||
7872 |
d5w_t f = (d5w_t) func; |
||
7873 |
r = f(W(args[0]), W(args[1]), W(args[2]), W(args[3]), W(args[4])); |
||
7874 |
} else if (nargs == 6) { |
||
7875 |
d6w_t f = (d6w_t) func; |
||
7876 |
r = f(W(args[0]), W(args[1]), W(args[2]), W(args[3]), W(args[4]), |
||
7877 |
W(args[5])); |
||
7878 |
} else { |
||
7879 |
abort(); |
||
7880 |
} |
||
7881 |
} else { |
||
7882 |
switch (nargs) { |
||
7883 |
case 0: |
||
7884 |
case 1: |
||
7885 |
case 2: |
||
7886 |
if (IS_D(args[0]) && IS_D(args[1])) { |
||
7887 |
ddd_t f = (ddd_t) func; |
||
7888 |
r = f(D(args[0]), D(args[1])); |
||
7889 |
} else if (IS_D(args[0])) { |
||
7890 |
ddw_t f = (ddw_t) func; |
||
7891 |
r = f(D(args[0]), W(args[1])); |
||
7892 |
} else { |
||
7893 |
dwd_t f = (dwd_t) func; |
||
7894 |
r = f(W(args[0]), D(args[1])); |
||
7895 |
} |
||
7896 |
break; |
||
7897 |
|||
7898 |
case 3: |
||
7899 |
if (IS_W(args[0]) && IS_W(args[1]) && IS_D(args[2])) { |
||
7900 |
dwwd_t f = (dwwd_t) func; |
||
7901 |
r = f(W(args[0]), W(args[1]), D(args[2])); |
||
7902 |
} else if (IS_W(args[0]) && IS_D(args[1]) && IS_W(args[2])) { |
||
7903 |
dwdw_t f = (dwdw_t) func; |
||
7904 |
r = f(W(args[0]), D(args[1]), W(args[2])); |
||
7905 |
} else if (IS_W(args[0]) && IS_D(args[1]) && IS_D(args[2])) { |
||
7906 |
dwdd_t f = (dwdd_t) func; |
||
7907 |
r = f(W(args[0]), D(args[1]), D(args[2])); |
||
7908 |
} else if (IS_D(args[0]) && IS_W(args[1]) && IS_W(args[2])) { |
||
7909 |
ddww_t f = (ddww_t) func; |
||
7910 |
r = f(D(args[0]), W(args[1]), W(args[2])); |
||
7911 |
} else if (IS_D(args[0]) && IS_W(args[1]) && IS_D(args[2])) { |
||
7912 |
ddwd_t f = (ddwd_t) func; |
||
7913 |
r = f(D(args[0]), W(args[1]), D(args[2])); |
||
7914 |
} else if (IS_D(args[0]) && IS_D(args[1]) && IS_W(args[2])) { |
||
7915 |
dddw_t f = (dddw_t) func; |
||
7916 |
r = f(D(args[0]), D(args[1]), W(args[2])); |
||
7917 |
} else if (IS_D(args[0]) && IS_D(args[1]) && IS_D(args[2])) { |
||
7918 |
dddd_t f = (dddd_t) func; |
||
7919 |
r = f(D(args[0]), D(args[1]), D(args[2])); |
||
7920 |
} else { |
||
7921 |
// The above checks should be exhaustive |
||
7922 |
abort(); |
||
7923 |
} |
||
7924 |
break; |
||
7925 |
default: |
||
7926 |
return -1; |
||
7927 |
} |
||
7928 |
} |
||
7929 |
res->v.d = r; |
||
7930 |
} break; /* }}} */ |
||
7931 |
case FFI_CTYPE_FLOAT: { /* {{{ */ |
||
7932 |
double r; |
||
7933 |
if (floats == 0) { |
||
7934 |
/* No float args: we currently support up to 6 word-sized arguments |
||
7935 |
*/ |
||
7936 |
if (nargs <= 4) { |
||
7937 |
f4w_t f = (f4w_t) func; |
||
7938 |
r = f(W(args[0]), W(args[1]), W(args[2]), W(args[3])); |
||
7939 |
} else if (nargs == 5) { |
||
7940 |
f5w_t f = (f5w_t) func; |
||
7941 |
r = f(W(args[0]), W(args[1]), W(args[2]), W(args[3]), W(args[4])); |
||
7942 |
} else if (nargs == 6) { |
||
7943 |
f6w_t f = (f6w_t) func; |
||
7944 |
r = f(W(args[0]), W(args[1]), W(args[2]), W(args[3]), W(args[4]), |
||
7945 |
W(args[5])); |
||
7946 |
} else { |
||
7947 |
abort(); |
||
7948 |
} |
||
7949 |
} else { |
||
7950 |
/* There are some float args */ |
||
7951 |
switch (nargs) { |
||
7952 |
case 0: |
||
7953 |
case 1: |
||
7954 |
case 2: |
||
7955 |
if (IS_F(args[0]) && IS_F(args[1])) { |
||
7956 |
fff_t f = (fff_t) func; |
||
7957 |
r = f(F(args[0]), F(args[1])); |
||
7958 |
} else if (IS_F(args[0])) { |
||
7959 |
ffw_t f = (ffw_t) func; |
||
7960 |
r = f(F(args[0]), W(args[1])); |
||
7961 |
} else { |
||
7962 |
fwf_t f = (fwf_t) func; |
||
7963 |
r = f(W(args[0]), F(args[1])); |
||
7964 |
} |
||
7965 |
break; |
||
7966 |
|||
7967 |
case 3: |
||
7968 |
if (IS_W(args[0]) && IS_W(args[1]) && IS_F(args[2])) { |
||
7969 |
fwwf_t f = (fwwf_t) func; |
||
7970 |
r = f(W(args[0]), W(args[1]), F(args[2])); |
||
7971 |
} else if (IS_W(args[0]) && IS_F(args[1]) && IS_W(args[2])) { |
||
7972 |
fwfw_t f = (fwfw_t) func; |
||
7973 |
r = f(W(args[0]), F(args[1]), W(args[2])); |
||
7974 |
} else if (IS_W(args[0]) && IS_F(args[1]) && IS_F(args[2])) { |
||
7975 |
fwff_t f = (fwff_t) func; |
||
7976 |
r = f(W(args[0]), F(args[1]), F(args[2])); |
||
7977 |
} else if (IS_F(args[0]) && IS_W(args[1]) && IS_W(args[2])) { |
||
7978 |
ffww_t f = (ffww_t) func; |
||
7979 |
r = f(F(args[0]), W(args[1]), W(args[2])); |
||
7980 |
} else if (IS_F(args[0]) && IS_W(args[1]) && IS_F(args[2])) { |
||
7981 |
ffwf_t f = (ffwf_t) func; |
||
7982 |
r = f(F(args[0]), W(args[1]), F(args[2])); |
||
7983 |
} else if (IS_F(args[0]) && IS_F(args[1]) && IS_W(args[2])) { |
||
7984 |
fffw_t f = (fffw_t) func; |
||
7985 |
r = f(F(args[0]), F(args[1]), W(args[2])); |
||
7986 |
} else if (IS_F(args[0]) && IS_F(args[1]) && IS_F(args[2])) { |
||
7987 |
ffff_t f = (ffff_t) func; |
||
7988 |
r = f(F(args[0]), F(args[1]), F(args[2])); |
||
7989 |
} else { |
||
7990 |
// The above checks should be exhaustive |
||
7991 |
abort(); |
||
7992 |
} |
||
7993 |
break; |
||
7994 |
default: |
||
7995 |
return -1; |
||
7996 |
} |
||
7997 |
} |
||
7998 |
res->v.f = r; |
||
7999 |
} break; /* }}} */ |
||
8000 |
} |
||
8001 |
|||
8002 |
return 0; |
||
8003 |
} |
||
8004 |
#ifdef MJS_MODULE_LINES |
||
8005 |
#line 1 "mjs/src/mjs_array.c" |
||
8006 |
#endif |
||
8007 |
/* |
||
8008 |
* Copyright (c) 2017 Cesanta Software Limited |
||
8009 |
* All rights reserved |
||
8010 |
*/ |
||
8011 |
|||
8012 |
#include <stdio.h> |
||
8013 |
/* Amalgamated: #include "common/str_util.h" */ |
||
8014 |
/* Amalgamated: #include "mjs/src/mjs_array.h" */ |
||
8015 |
/* Amalgamated: #include "mjs/src/mjs_conversion.h" */ |
||
8016 |
/* Amalgamated: #include "mjs/src/mjs_core.h" */ |
||
8017 |
/* Amalgamated: #include "mjs/src/mjs_internal.h" */ |
||
8018 |
/* Amalgamated: #include "mjs/src/mjs_object.h" */ |
||
8019 |
/* Amalgamated: #include "mjs/src/mjs_primitive.h" */ |
||
8020 |
/* Amalgamated: #include "mjs/src/mjs_string.h" */ |
||
8021 |
/* Amalgamated: #include "mjs/src/mjs_util.h" */ |
||
8022 |
|||
8023 |
#define SPLICE_NEW_ITEM_IDX 2 |
||
8024 |
|||
8025 |
/* like c_snprintf but returns `size` if write is truncated */ |
||
8026 |
static int v_sprintf_s(char *buf, size_t size, const char *fmt, ...) { |
||
8027 |
size_t n; |
||
8028 |
va_list ap; |
||
8029 |
va_start(ap, fmt); |
||
8030 |
n = c_vsnprintf(buf, size, fmt, ap); |
||
8031 |
if (n > size) { |
||
8032 |
return size; |
||
8033 |
} |
||
8034 |
return n; |
||
8035 |
} |
||
8036 |
|||
8037 |
mjs_val_t mjs_mk_array(struct mjs *mjs) { |
||
8038 |
mjs_val_t ret = mjs_mk_object(mjs); |
||
8039 |
/* change the tag to MJS_TAG_ARRAY */ |
||
8040 |
ret &= ~MJS_TAG_MASK; |
||
8041 |
ret |= MJS_TAG_ARRAY; |
||
8042 |
return ret; |
||
8043 |
} |
||
8044 |
|||
8045 |
56 |
int mjs_is_array(mjs_val_t v) { |
|
8046 |
56 |
return (v & MJS_TAG_MASK) == MJS_TAG_ARRAY; |
|
8047 |
} |
||
8048 |
|||
8049 |
mjs_val_t mjs_array_get(struct mjs *mjs, mjs_val_t arr, unsigned long index) { |
||
8050 |
return mjs_array_get2(mjs, arr, index, NULL); |
||
8051 |
} |
||
8052 |
|||
8053 |
mjs_val_t mjs_array_get2(struct mjs *mjs, mjs_val_t arr, unsigned long index, |
||
8054 |
int *has) { |
||
8055 |
mjs_val_t res = MJS_UNDEFINED; |
||
8056 |
|||
8057 |
if (has != NULL) { |
||
8058 |
*has = 0; |
||
8059 |
} |
||
8060 |
|||
8061 |
if (mjs_is_object(arr)) { |
||
8062 |
struct mjs_property *p; |
||
8063 |
char buf[20]; |
||
8064 |
int n = v_sprintf_s(buf, sizeof(buf), "%lu", index); |
||
8065 |
p = mjs_get_own_property(mjs, arr, buf, n); |
||
8066 |
if (p != NULL) { |
||
8067 |
if (has != NULL) { |
||
8068 |
*has = 1; |
||
8069 |
} |
||
8070 |
res = p->value; |
||
8071 |
} |
||
8072 |
} |
||
8073 |
|||
8074 |
return res; |
||
8075 |
} |
||
8076 |
|||
8077 |
unsigned long mjs_array_length(struct mjs *mjs, mjs_val_t v) { |
||
8078 |
struct mjs_property *p; |
||
8079 |
unsigned long len = 0; |
||
8080 |
|||
8081 |
if (!mjs_is_object(v)) { |
||
8082 |
len = 0; |
||
8083 |
goto clean; |
||
8084 |
} |
||
8085 |
|||
8086 |
for (p = get_object_struct(v)->properties; p != NULL; p = p->next) { |
||
8087 |
int ok = 0; |
||
8088 |
unsigned long n = 0; |
||
8089 |
str_to_ulong(mjs, p->name, &ok, &n); |
||
8090 |
if (ok && n >= len && n < 0xffffffff) { |
||
8091 |
len = n + 1; |
||
8092 |
} |
||
8093 |
} |
||
8094 |
|||
8095 |
clean: |
||
8096 |
return len; |
||
8097 |
} |
||
8098 |
|||
8099 |
mjs_err_t mjs_array_set(struct mjs *mjs, mjs_val_t arr, unsigned long index, |
||
8100 |
mjs_val_t v) { |
||
8101 |
mjs_err_t ret = MJS_OK; |
||
8102 |
|||
8103 |
if (mjs_is_object(arr)) { |
||
8104 |
char buf[20]; |
||
8105 |
int n = v_sprintf_s(buf, sizeof(buf), "%lu", index); |
||
8106 |
ret = mjs_set(mjs, arr, buf, n, v); |
||
8107 |
} else { |
||
8108 |
ret = MJS_TYPE_ERROR; |
||
8109 |
} |
||
8110 |
|||
8111 |
return ret; |
||
8112 |
} |
||
8113 |
|||
8114 |
void mjs_array_del(struct mjs *mjs, mjs_val_t arr, unsigned long index) { |
||
8115 |
char buf[20]; |
||
8116 |
int n = v_sprintf_s(buf, sizeof(buf), "%lu", index); |
||
8117 |
mjs_del(mjs, arr, buf, n); |
||
8118 |
} |
||
8119 |
|||
8120 |
mjs_err_t mjs_array_push(struct mjs *mjs, mjs_val_t arr, mjs_val_t v) { |
||
8121 |
return mjs_array_set(mjs, arr, mjs_array_length(mjs, arr), v); |
||
8122 |
} |
||
8123 |
|||
8124 |
MJS_PRIVATE void mjs_array_push_internal(struct mjs *mjs) { |
||
8125 |
mjs_err_t rcode = MJS_OK; |
||
8126 |
mjs_val_t ret = MJS_UNDEFINED; |
||
8127 |
int nargs = mjs_nargs(mjs); |
||
8128 |
int i; |
||
8129 |
|||
8130 |
/* Make sure that `this` is an array */ |
||
8131 |
if (!mjs_check_arg(mjs, -1 /*this*/, "this", MJS_TYPE_OBJECT_ARRAY, NULL)) { |
||
8132 |
goto clean; |
||
8133 |
} |
||
8134 |
|||
8135 |
/* Push all args */ |
||
8136 |
for (i = 0; i < nargs; i++) { |
||
8137 |
rcode = mjs_array_push(mjs, mjs->vals.this_obj, mjs_arg(mjs, i)); |
||
8138 |
if (rcode != MJS_OK) { |
||
8139 |
mjs_prepend_errorf(mjs, rcode, ""); |
||
8140 |
goto clean; |
||
8141 |
} |
||
8142 |
} |
||
8143 |
|||
8144 |
/* Return the new array length */ |
||
8145 |
ret = mjs_mk_number(mjs, mjs_array_length(mjs, mjs->vals.this_obj)); |
||
8146 |
|||
8147 |
clean: |
||
8148 |
mjs_return(mjs, ret); |
||
8149 |
return; |
||
8150 |
} |
||
8151 |
|||
8152 |
static void move_item(struct mjs *mjs, mjs_val_t arr, unsigned long from, |
||
8153 |
unsigned long to) { |
||
8154 |
mjs_val_t cur = mjs_array_get(mjs, arr, from); |
||
8155 |
mjs_array_set(mjs, arr, to, cur); |
||
8156 |
mjs_array_del(mjs, arr, from); |
||
8157 |
} |
||
8158 |
|||
8159 |
MJS_PRIVATE void mjs_array_splice(struct mjs *mjs) { |
||
8160 |
int nargs = mjs_nargs(mjs); |
||
8161 |
mjs_err_t rcode = MJS_OK; |
||
8162 |
mjs_val_t ret = mjs_mk_array(mjs); |
||
8163 |
mjs_val_t start_v = MJS_UNDEFINED; |
||
8164 |
mjs_val_t deleteCount_v = MJS_UNDEFINED; |
||
8165 |
int start = 0; |
||
8166 |
int arr_len; |
||
8167 |
int delete_cnt = 0; |
||
8168 |
int new_items_cnt = 0; |
||
8169 |
int delta = 0; |
||
8170 |
int i; |
||
8171 |
|||
8172 |
/* Make sure that `this` is an array */ |
||
8173 |
if (!mjs_check_arg(mjs, -1 /*this*/, "this", MJS_TYPE_OBJECT_ARRAY, NULL)) { |
||
8174 |
goto clean; |
||
8175 |
} |
||
8176 |
|||
8177 |
/* Get array length */ |
||
8178 |
arr_len = mjs_array_length(mjs, mjs->vals.this_obj); |
||
8179 |
|||
8180 |
/* get start from arg 0 */ |
||
8181 |
if (!mjs_check_arg(mjs, 0, "start", MJS_TYPE_NUMBER, &start_v)) { |
||
8182 |
goto clean; |
||
8183 |
} |
||
8184 |
start = mjs_normalize_idx(mjs_get_int(mjs, start_v), arr_len); |
||
8185 |
|||
8186 |
/* Handle deleteCount */ |
||
8187 |
if (nargs >= SPLICE_NEW_ITEM_IDX) { |
||
8188 |
/* deleteCount is given; use it */ |
||
8189 |
if (!mjs_check_arg(mjs, 1, "deleteCount", MJS_TYPE_NUMBER, |
||
8190 |
&deleteCount_v)) { |
||
8191 |
goto clean; |
||
8192 |
} |
||
8193 |
delete_cnt = mjs_get_int(mjs, deleteCount_v); |
||
8194 |
new_items_cnt = nargs - SPLICE_NEW_ITEM_IDX; |
||
8195 |
} else { |
||
8196 |
/* deleteCount is not given; assume the end of the array */ |
||
8197 |
delete_cnt = arr_len - start; |
||
8198 |
} |
||
8199 |
if (delete_cnt > arr_len - start) { |
||
8200 |
delete_cnt = arr_len - start; |
||
8201 |
} else if (delete_cnt < 0) { |
||
8202 |
delete_cnt = 0; |
||
8203 |
} |
||
8204 |
|||
8205 |
/* delta at which subsequent array items should be moved */ |
||
8206 |
delta = new_items_cnt - delete_cnt; |
||
8207 |
|||
8208 |
/* |
||
8209 |
* copy items which are going to be deleted to the separate array (will be |
||
8210 |
* returned) |
||
8211 |
*/ |
||
8212 |
for (i = 0; i < delete_cnt; i++) { |
||
8213 |
mjs_val_t cur = mjs_array_get(mjs, mjs->vals.this_obj, start + i); |
||
8214 |
rcode = mjs_array_push(mjs, ret, cur); |
||
8215 |
if (rcode != MJS_OK) { |
||
8216 |
mjs_prepend_errorf(mjs, rcode, ""); |
||
8217 |
goto clean; |
||
8218 |
} |
||
8219 |
} |
||
8220 |
|||
8221 |
/* If needed, move subsequent items */ |
||
8222 |
if (delta < 0) { |
||
8223 |
for (i = start; i < arr_len; i++) { |
||
8224 |
if (i >= start - delta) { |
||
8225 |
move_item(mjs, mjs->vals.this_obj, i, i + delta); |
||
8226 |
} else { |
||
8227 |
mjs_array_del(mjs, mjs->vals.this_obj, i); |
||
8228 |
} |
||
8229 |
} |
||
8230 |
} else if (delta > 0) { |
||
8231 |
for (i = arr_len - 1; i >= start; i--) { |
||
8232 |
move_item(mjs, mjs->vals.this_obj, i, i + delta); |
||
8233 |
} |
||
8234 |
} |
||
8235 |
|||
8236 |
/* Set new items to the array */ |
||
8237 |
for (i = 0; i < nargs - SPLICE_NEW_ITEM_IDX; i++) { |
||
8238 |
mjs_array_set(mjs, mjs->vals.this_obj, start + i, |
||
8239 |
mjs_arg(mjs, SPLICE_NEW_ITEM_IDX + i)); |
||
8240 |
} |
||
8241 |
|||
8242 |
clean: |
||
8243 |
mjs_return(mjs, ret); |
||
8244 |
} |
||
8245 |
#ifdef MJS_MODULE_LINES |
||
8246 |
#line 1 "mjs/src/mjs_bcode.c" |
||
8247 |
#endif |
||
8248 |
/* |
||
8249 |
* Copyright (c) 2017 Cesanta Software Limited |
||
8250 |
* All rights reserved |
||
8251 |
*/ |
||
8252 |
|||
8253 |
/* Amalgamated: #include "common/cs_varint.h" */ |
||
8254 |
|||
8255 |
/* Amalgamated: #include "mjs/src/mjs_internal.h" */ |
||
8256 |
/* Amalgamated: #include "mjs/src/mjs_bcode.h" */ |
||
8257 |
/* Amalgamated: #include "mjs/src/mjs_core.h" */ |
||
8258 |
/* Amalgamated: #include "mjs/src/mjs_tok.h" */ |
||
8259 |
|||
8260 |
780 |
static void add_lineno_map_item(struct pstate *pstate) { |
|
8261 |
✓✓ | 780 |
if (pstate->last_emitted_line_no < pstate->line_no) { |
8262 |
9 |
int offset = pstate->cur_idx - pstate->start_bcode_idx; |
|
8263 |
9 |
size_t offset_llen = cs_varint_llen(offset); |
|
8264 |
9 |
size_t lineno_llen = cs_varint_llen(pstate->line_no); |
|
8265 |
9 |
mbuf_resize(&pstate->offset_lineno_map, |
|
8266 |
9 |
pstate->offset_lineno_map.size + offset_llen + lineno_llen); |
|
8267 |
|||
8268 |
/* put offset */ |
||
8269 |
9 |
cs_varint_encode(offset, (uint8_t *) pstate->offset_lineno_map.buf + |
|
8270 |
pstate->offset_lineno_map.len, |
||
8271 |
offset_llen); |
||
8272 |
9 |
pstate->offset_lineno_map.len += offset_llen; |
|
8273 |
|||
8274 |
/* put line_no */ |
||
8275 |
9 |
cs_varint_encode(pstate->line_no, |
|
8276 |
9 |
(uint8_t *) pstate->offset_lineno_map.buf + |
|
8277 |
pstate->offset_lineno_map.len, |
||
8278 |
lineno_llen); |
||
8279 |
9 |
pstate->offset_lineno_map.len += lineno_llen; |
|
8280 |
|||
8281 |
9 |
pstate->last_emitted_line_no = pstate->line_no; |
|
8282 |
} |
||
8283 |
780 |
} |
|
8284 |
|||
8285 |
663 |
MJS_PRIVATE void emit_byte(struct pstate *pstate, uint8_t byte) { |
|
8286 |
663 |
add_lineno_map_item(pstate); |
|
8287 |
663 |
mbuf_insert(&pstate->mjs->bcode_gen, pstate->cur_idx, &byte, sizeof(byte)); |
|
8288 |
663 |
pstate->cur_idx += sizeof(byte); |
|
8289 |
663 |
} |
|
8290 |
|||
8291 |
42 |
MJS_PRIVATE void emit_int(struct pstate *pstate, int64_t n) { |
|
8292 |
42 |
struct mbuf *b = &pstate->mjs->bcode_gen; |
|
8293 |
42 |
size_t llen = cs_varint_llen(n); |
|
8294 |
42 |
add_lineno_map_item(pstate); |
|
8295 |
42 |
mbuf_insert(b, pstate->cur_idx, NULL, llen); |
|
8296 |
42 |
cs_varint_encode(n, (uint8_t *) b->buf + pstate->cur_idx, llen); |
|
8297 |
42 |
pstate->cur_idx += llen; |
|
8298 |
42 |
} |
|
8299 |
|||
8300 |
75 |
MJS_PRIVATE void emit_str(struct pstate *pstate, const char *ptr, size_t len) { |
|
8301 |
75 |
struct mbuf *b = &pstate->mjs->bcode_gen; |
|
8302 |
75 |
size_t llen = cs_varint_llen(len); |
|
8303 |
75 |
add_lineno_map_item(pstate); |
|
8304 |
75 |
mbuf_insert(b, pstate->cur_idx, NULL, llen + len); |
|
8305 |
75 |
cs_varint_encode(len, (uint8_t *) b->buf + pstate->cur_idx, llen); |
|
8306 |
75 |
memcpy(b->buf + pstate->cur_idx + llen, ptr, len); |
|
8307 |
75 |
pstate->cur_idx += llen + len; |
|
8308 |
75 |
} |
|
8309 |
|||
8310 |
10 |
MJS_PRIVATE int mjs_bcode_insert_offset(struct pstate *p, struct mjs *mjs, |
|
8311 |
size_t offset, size_t v) { |
||
8312 |
10 |
int llen = (int) cs_varint_llen(v); |
|
8313 |
10 |
int diff = llen - MJS_INIT_OFFSET_SIZE; |
|
8314 |
✗✓ | 10 |
assert(offset < mjs->bcode_gen.len); |
8315 |
✗✓ | 10 |
if (diff > 0) { |
8316 |
mbuf_resize(&mjs->bcode_gen, mjs->bcode_gen.size + diff); |
||
8317 |
} |
||
8318 |
/* |
||
8319 |
* Offset is going to take more than one was reserved, so, move the data |
||
8320 |
* forward |
||
8321 |
*/ |
||
8322 |
20 |
memmove(mjs->bcode_gen.buf + offset + llen, |
|
8323 |
10 |
mjs->bcode_gen.buf + offset + MJS_INIT_OFFSET_SIZE, |
|
8324 |
10 |
mjs->bcode_gen.len - offset - MJS_INIT_OFFSET_SIZE); |
|
8325 |
10 |
mjs->bcode_gen.len += diff; |
|
8326 |
10 |
cs_varint_encode(v, (uint8_t *) mjs->bcode_gen.buf + offset, llen); |
|
8327 |
|||
8328 |
/* |
||
8329 |
* If current parsing index is after the offset at which we've inserted new |
||
8330 |
* varint, the index might need to be adjusted |
||
8331 |
*/ |
||
8332 |
✓✗ | 10 |
if (p->cur_idx >= (int) offset) { |
8333 |
10 |
p->cur_idx += diff; |
|
8334 |
} |
||
8335 |
10 |
return diff; |
|
8336 |
} |
||
8337 |
|||
8338 |
66 |
MJS_PRIVATE void mjs_bcode_part_add(struct mjs *mjs, |
|
8339 |
const struct mjs_bcode_part *bp) { |
||
8340 |
66 |
mbuf_append(&mjs->bcode_parts, bp, sizeof(*bp)); |
|
8341 |
66 |
} |
|
8342 |
|||
8343 |
298 |
MJS_PRIVATE struct mjs_bcode_part *mjs_bcode_part_get(struct mjs *mjs, |
|
8344 |
int num) { |
||
8345 |
✗✓ | 298 |
assert(num < mjs_bcode_parts_cnt(mjs)); |
8346 |
298 |
return (struct mjs_bcode_part *) (mjs->bcode_parts.buf + |
|
8347 |
num * sizeof(struct mjs_bcode_part)); |
||
8348 |
} |
||
8349 |
|||
8350 |
232 |
MJS_PRIVATE struct mjs_bcode_part *mjs_bcode_part_get_by_offset(struct mjs *mjs, |
|
8351 |
size_t offset) { |
||
8352 |
int i; |
||
8353 |
232 |
int parts_cnt = mjs_bcode_parts_cnt(mjs); |
|
8354 |
232 |
struct mjs_bcode_part *bp = NULL; |
|
8355 |
|||
8356 |
✗✓ | 232 |
if (offset >= mjs->bcode_len) { |
8357 |
return NULL; |
||
8358 |
} |
||
8359 |
|||
8360 |
✓✗ | 232 |
for (i = 0; i < parts_cnt; i++) { |
8361 |
232 |
bp = mjs_bcode_part_get(mjs, i); |
|
8362 |
✓✗ | 232 |
if (offset < bp->start_idx + bp->data.len) { |
8363 |
232 |
break; |
|
8364 |
} |
||
8365 |
} |
||
8366 |
|||
8367 |
/* given the non-corrupted data, the needed part must be found */ |
||
8368 |
✗✓ | 232 |
assert(i < parts_cnt); |
8369 |
|||
8370 |
232 |
return bp; |
|
8371 |
} |
||
8372 |
|||
8373 |
608 |
MJS_PRIVATE int mjs_bcode_parts_cnt(struct mjs *mjs) { |
|
8374 |
608 |
return mjs->bcode_parts.len / sizeof(struct mjs_bcode_part); |
|
8375 |
} |
||
8376 |
|||
8377 |
66 |
MJS_PRIVATE void mjs_bcode_commit(struct mjs *mjs) { |
|
8378 |
struct mjs_bcode_part bp; |
||
8379 |
66 |
memset(&bp, 0, sizeof(bp)); |
|
8380 |
|||
8381 |
/* Make sure the bcode doesn't occupy any extra space */ |
||
8382 |
66 |
mbuf_trim(&mjs->bcode_gen); |
|
8383 |
|||
8384 |
/* Transfer the ownership of the bcode data */ |
||
8385 |
66 |
bp.data.p = mjs->bcode_gen.buf; |
|
8386 |
66 |
bp.data.len = mjs->bcode_gen.len; |
|
8387 |
66 |
mbuf_init(&mjs->bcode_gen, 0); |
|
8388 |
|||
8389 |
66 |
bp.start_idx = mjs->bcode_len; |
|
8390 |
66 |
bp.exec_res = MJS_ERRS_CNT; |
|
8391 |
|||
8392 |
66 |
mjs_bcode_part_add(mjs, &bp); |
|
8393 |
|||
8394 |
66 |
mjs->bcode_len += bp.data.len; |
|
8395 |
66 |
} |
|
8396 |
#ifdef MJS_MODULE_LINES |
||
8397 |
#line 1 "mjs/src/mjs_builtin.c" |
||
8398 |
#endif |
||
8399 |
/* |
||
8400 |
* Copyright (c) 2017 Cesanta Software Limited |
||
8401 |
* All rights reserved |
||
8402 |
*/ |
||
8403 |
|||
8404 |
/* Amalgamated: #include "mjs/src/mjs_bcode.h" */ |
||
8405 |
/* Amalgamated: #include "mjs/src/mjs_core.h" */ |
||
8406 |
/* Amalgamated: #include "mjs/src/mjs_dataview.h" */ |
||
8407 |
/* Amalgamated: #include "mjs/src/mjs_exec.h" */ |
||
8408 |
/* Amalgamated: #include "mjs/src/mjs_internal.h" */ |
||
8409 |
/* Amalgamated: #include "mjs/src/mjs_json.h" */ |
||
8410 |
/* Amalgamated: #include "mjs/src/mjs_object.h" */ |
||
8411 |
/* Amalgamated: #include "mjs/src/mjs_primitive.h" */ |
||
8412 |
/* Amalgamated: #include "mjs/src/mjs_string.h" */ |
||
8413 |
/* Amalgamated: #include "mjs/src/mjs_util.h" */ |
||
8414 |
|||
8415 |
static void mjs_print(struct mjs *mjs) { |
||
8416 |
size_t i, num_args = mjs_nargs(mjs); |
||
8417 |
for (i = 0; i < num_args; i++) { |
||
8418 |
mjs_fprintf(mjs_arg(mjs, i), mjs, stdout); |
||
8419 |
putchar(' '); |
||
8420 |
} |
||
8421 |
putchar('\n'); |
||
8422 |
mjs_return(mjs, MJS_UNDEFINED); |
||
8423 |
} |
||
8424 |
|||
8425 |
/* |
||
8426 |
* If the file with the given filename was already loaded, returns the |
||
8427 |
* corresponding bcode part; otherwise returns NULL. |
||
8428 |
*/ |
||
8429 |
static struct mjs_bcode_part *mjs_get_loaded_file_bcode(struct mjs *mjs, |
||
8430 |
const char *filename) { |
||
8431 |
int parts_cnt = mjs_bcode_parts_cnt(mjs); |
||
8432 |
int i; |
||
8433 |
|||
8434 |
if (filename == NULL) { |
||
8435 |
return 0; |
||
8436 |
} |
||
8437 |
|||
8438 |
for (i = 0; i < parts_cnt; i++) { |
||
8439 |
struct mjs_bcode_part *bp = mjs_bcode_part_get(mjs, i); |
||
8440 |
const char *cur_fn = mjs_get_bcode_filename(mjs, bp); |
||
8441 |
if (strcmp(filename, cur_fn) == 0) { |
||
8442 |
return bp; |
||
8443 |
} |
||
8444 |
} |
||
8445 |
return NULL; |
||
8446 |
} |
||
8447 |
|||
8448 |
static void mjs_load(struct mjs *mjs) { |
||
8449 |
mjs_val_t res = MJS_UNDEFINED; |
||
8450 |
mjs_val_t arg0 = mjs_arg(mjs, 0); |
||
8451 |
mjs_val_t arg1 = mjs_arg(mjs, 1); |
||
8452 |
int custom_global = 0; /* whether the custom global object was provided */ |
||
8453 |
|||
8454 |
if (mjs_is_string(arg0)) { |
||
8455 |
const char *path = mjs_get_cstring(mjs, &arg0); |
||
8456 |
struct mjs_bcode_part *bp = NULL; |
||
8457 |
mjs_err_t ret; |
||
8458 |
|||
8459 |
if (mjs_is_object(arg1)) { |
||
8460 |
custom_global = 1; |
||
8461 |
push_mjs_val(&mjs->scopes, arg1); |
||
8462 |
} |
||
8463 |
bp = mjs_get_loaded_file_bcode(mjs, path); |
||
8464 |
if (bp == NULL) { |
||
8465 |
/* File was not loaded before, so, load */ |
||
8466 |
ret = mjs_exec_file(mjs, path, &res); |
||
8467 |
} else { |
||
8468 |
/* |
||
8469 |
* File was already loaded before, so if it was evaluated successfully, |
||
8470 |
* then skip the evaluation at all (and assume MJS_OK); otherwise |
||
8471 |
* re-evaluate it again. |
||
8472 |
* |
||
8473 |
* However, if the custom global object was provided, then reevaluate |
||
8474 |
* the file in any case. |
||
8475 |
*/ |
||
8476 |
if (bp->exec_res != MJS_OK || custom_global) { |
||
8477 |
ret = mjs_execute(mjs, bp->start_idx, &res); |
||
8478 |
} else { |
||
8479 |
ret = MJS_OK; |
||
8480 |
} |
||
8481 |
} |
||
8482 |
if (ret != MJS_OK) { |
||
8483 |
/* |
||
8484 |
* arg0 and path might be invalidated by executing a file, so refresh |
||
8485 |
* them |
||
8486 |
*/ |
||
8487 |
arg0 = mjs_arg(mjs, 0); |
||
8488 |
path = mjs_get_cstring(mjs, &arg0); |
||
8489 |
mjs_prepend_errorf(mjs, ret, "failed to exec file \"%s\"", path); |
||
8490 |
goto clean; |
||
8491 |
} |
||
8492 |
|||
8493 |
clean: |
||
8494 |
if (custom_global) { |
||
8495 |
mjs_pop_val(&mjs->scopes); |
||
8496 |
} |
||
8497 |
} |
||
8498 |
mjs_return(mjs, res); |
||
8499 |
} |
||
8500 |
|||
8501 |
static void mjs_get_mjs(struct mjs *mjs) { |
||
8502 |
mjs_return(mjs, mjs_mk_foreign(mjs, mjs)); |
||
8503 |
} |
||
8504 |
|||
8505 |
static void mjs_chr(struct mjs *mjs) { |
||
8506 |
mjs_val_t arg0 = mjs_arg(mjs, 0), res = MJS_NULL; |
||
8507 |
int n = mjs_get_int(mjs, arg0); |
||
8508 |
if (mjs_is_number(arg0) && n >= 0 && n <= 255) { |
||
8509 |
uint8_t s = n; |
||
8510 |
res = mjs_mk_string(mjs, (const char *) &s, sizeof(s), 1); |
||
8511 |
} |
||
8512 |
mjs_return(mjs, res); |
||
8513 |
} |
||
8514 |
|||
8515 |
static void mjs_do_gc(struct mjs *mjs) { |
||
8516 |
mjs_val_t arg0 = mjs_arg(mjs, 0); |
||
8517 |
mjs_gc(mjs, mjs_is_boolean(arg0) ? mjs_get_bool(mjs, arg0) : 0); |
||
8518 |
mjs_return(mjs, arg0); |
||
8519 |
} |
||
8520 |
|||
8521 |
static void mjs_s2o(struct mjs *mjs) { |
||
8522 |
mjs_return(mjs, |
||
8523 |
mjs_struct_to_obj(mjs, mjs_get_ptr(mjs, mjs_arg(mjs, 0)), |
||
8524 |
(const struct mjs_c_struct_member *) mjs_get_ptr( |
||
8525 |
mjs, mjs_arg(mjs, 1)))); |
||
8526 |
} |
||
8527 |
|||
8528 |
78 |
void mjs_init_builtin(struct mjs *mjs, mjs_val_t obj) { |
|
8529 |
mjs_val_t v; |
||
8530 |
|||
8531 |
78 |
mjs_set(mjs, obj, "global", ~0, obj); |
|
8532 |
|||
8533 |
78 |
mjs_set(mjs, obj, "load", ~0, |
|
8534 |
mjs_mk_foreign_func(mjs, (mjs_func_ptr_t) mjs_load)); |
||
8535 |
78 |
mjs_set(mjs, obj, "print", ~0, |
|
8536 |
mjs_mk_foreign_func(mjs, (mjs_func_ptr_t) mjs_print)); |
||
8537 |
78 |
mjs_set(mjs, obj, "ffi", ~0, |
|
8538 |
mjs_mk_foreign_func(mjs, (mjs_func_ptr_t) mjs_ffi_call)); |
||
8539 |
78 |
mjs_set(mjs, obj, "ffi_cb_free", ~0, |
|
8540 |
mjs_mk_foreign_func(mjs, (mjs_func_ptr_t) mjs_ffi_cb_free)); |
||
8541 |
78 |
mjs_set(mjs, obj, "mkstr", ~0, |
|
8542 |
mjs_mk_foreign_func(mjs, (mjs_func_ptr_t) mjs_mkstr)); |
||
8543 |
78 |
mjs_set(mjs, obj, "getMJS", ~0, |
|
8544 |
mjs_mk_foreign_func(mjs, (mjs_func_ptr_t) mjs_get_mjs)); |
||
8545 |
78 |
mjs_set(mjs, obj, "die", ~0, |
|
8546 |
mjs_mk_foreign_func(mjs, (mjs_func_ptr_t) mjs_die)); |
||
8547 |
78 |
mjs_set(mjs, obj, "gc", ~0, |
|
8548 |
mjs_mk_foreign_func(mjs, (mjs_func_ptr_t) mjs_do_gc)); |
||
8549 |
78 |
mjs_set(mjs, obj, "chr", ~0, |
|
8550 |
mjs_mk_foreign_func(mjs, (mjs_func_ptr_t) mjs_chr)); |
||
8551 |
78 |
mjs_set(mjs, obj, "s2o", ~0, |
|
8552 |
mjs_mk_foreign_func(mjs, (mjs_func_ptr_t) mjs_s2o)); |
||
8553 |
|||
8554 |
/* |
||
8555 |
* Populate JSON.parse() and JSON.stringify() |
||
8556 |
*/ |
||
8557 |
78 |
v = mjs_mk_object(mjs); |
|
8558 |
78 |
mjs_set(mjs, v, "stringify", ~0, |
|
8559 |
mjs_mk_foreign_func(mjs, (mjs_func_ptr_t) mjs_op_json_stringify)); |
||
8560 |
78 |
mjs_set(mjs, v, "parse", ~0, |
|
8561 |
mjs_mk_foreign_func(mjs, (mjs_func_ptr_t) mjs_op_json_parse)); |
||
8562 |
78 |
mjs_set(mjs, obj, "JSON", ~0, v); |
|
8563 |
|||
8564 |
/* |
||
8565 |
* Populate Object.create() |
||
8566 |
*/ |
||
8567 |
78 |
v = mjs_mk_object(mjs); |
|
8568 |
78 |
mjs_set(mjs, v, "create", ~0, |
|
8569 |
mjs_mk_foreign_func(mjs, (mjs_func_ptr_t) mjs_op_create_object)); |
||
8570 |
78 |
mjs_set(mjs, obj, "Object", ~0, v); |
|
8571 |
|||
8572 |
/* |
||
8573 |
* Populate numeric stuff |
||
8574 |
*/ |
||
8575 |
78 |
mjs_set(mjs, obj, "NaN", ~0, MJS_TAG_NAN); |
|
8576 |
78 |
mjs_set(mjs, obj, "isNaN", ~0, |
|
8577 |
mjs_mk_foreign_func(mjs, (mjs_func_ptr_t) mjs_op_isnan)); |
||
8578 |
78 |
} |
|
8579 |
#ifdef MJS_MODULE_LINES |
||
8580 |
#line 1 "mjs/src/mjs_conversion.c" |
||
8581 |
#endif |
||
8582 |
/* |
||
8583 |
* Copyright (c) 2016 Cesanta Software Limited |
||
8584 |
* All rights reserved |
||
8585 |
*/ |
||
8586 |
|||
8587 |
/* Amalgamated: #include "mjs/src/mjs_conversion.h" */ |
||
8588 |
/* Amalgamated: #include "mjs/src/mjs_object.h" */ |
||
8589 |
/* Amalgamated: #include "mjs/src/mjs_primitive.h" */ |
||
8590 |
/* Amalgamated: #include "mjs/src/mjs_string.h" */ |
||
8591 |
/* Amalgamated: #include "mjs/src/mjs_util.h" */ |
||
8592 |
|||
8593 |
42 |
MJS_PRIVATE mjs_err_t mjs_to_string(struct mjs *mjs, mjs_val_t *v, char **p, |
|
8594 |
size_t *sizep, int *need_free) { |
||
8595 |
42 |
mjs_err_t ret = MJS_OK; |
|
8596 |
|||
8597 |
42 |
*p = NULL; |
|
8598 |
42 |
*sizep = 0; |
|
8599 |
42 |
*need_free = 0; |
|
8600 |
|||
8601 |
✓✗ | 42 |
if (mjs_is_string(*v)) { |
8602 |
42 |
*p = (char *) mjs_get_string(mjs, v, sizep); |
|
8603 |
} else if (mjs_is_number(*v)) { |
||
8604 |
char buf[50] = ""; |
||
8605 |
struct json_out out = JSON_OUT_BUF(buf, sizeof(buf)); |
||
8606 |
mjs_jprintf(*v, mjs, &out); |
||
8607 |
*sizep = strlen(buf); |
||
8608 |
*p = malloc(*sizep + 1); |
||
8609 |
if (*p == NULL) { |
||
8610 |
ret = MJS_OUT_OF_MEMORY; |
||
8611 |
goto clean; |
||
8612 |
} |
||
8613 |
memmove(*p, buf, *sizep+1); |
||
8614 |
*need_free = 1; |
||
8615 |
} else if (mjs_is_boolean(*v)) { |
||
8616 |
if (mjs_get_bool(mjs, *v)) { |
||
8617 |
*p = "true"; |
||
8618 |
*sizep = 4; |
||
8619 |
} else { |
||
8620 |
*p = "false"; |
||
8621 |
*sizep = 5; |
||
8622 |
} |
||
8623 |
} else if (mjs_is_undefined(*v)) { |
||
8624 |
*p = "undefined"; |
||
8625 |
*sizep = 9; |
||
8626 |
} else if (mjs_is_null(*v)) { |
||
8627 |
*p = "null"; |
||
8628 |
*sizep = 4; |
||
8629 |
} else if (mjs_is_object(*v)) { |
||
8630 |
ret = MJS_TYPE_ERROR; |
||
8631 |
mjs_set_errorf(mjs, ret, |
||
8632 |
"conversion from object to string is not supported"); |
||
8633 |
} else if (mjs_is_foreign(*v)) { |
||
8634 |
*p = "TODO_foreign"; |
||
8635 |
*sizep = 12; |
||
8636 |
} else { |
||
8637 |
ret = MJS_TYPE_ERROR; |
||
8638 |
mjs_set_errorf(mjs, ret, "unknown type to convert to string"); |
||
8639 |
} |
||
8640 |
|||
8641 |
clean: |
||
8642 |
42 |
return ret; |
|
8643 |
} |
||
8644 |
|||
8645 |
MJS_PRIVATE mjs_val_t mjs_to_boolean_v(struct mjs *mjs, mjs_val_t v) { |
||
8646 |
size_t len; |
||
8647 |
int is_truthy; |
||
8648 |
|||
8649 |
is_truthy = |
||
8650 |
((mjs_is_boolean(v) && mjs_get_bool(mjs, v)) || |
||
8651 |
(mjs_is_number(v) && mjs_get_double(mjs, v) != 0.0) || |
||
8652 |
(mjs_is_string(v) && mjs_get_string(mjs, &v, &len) && len > 0) || |
||
8653 |
(mjs_is_function(v)) || (mjs_is_foreign(v)) || (mjs_is_object(v))) && |
||
8654 |
v != MJS_TAG_NAN; |
||
8655 |
|||
8656 |
return mjs_mk_boolean(mjs, is_truthy); |
||
8657 |
} |
||
8658 |
|||
8659 |
MJS_PRIVATE int mjs_is_truthy(struct mjs *mjs, mjs_val_t v) { |
||
8660 |
return mjs_get_bool(mjs, mjs_to_boolean_v(mjs, v)); |
||
8661 |
} |
||
8662 |
#ifdef MJS_MODULE_LINES |
||
8663 |
#line 1 "mjs/src/mjs_core.c" |
||
8664 |
#endif |
||
8665 |
/* |
||
8666 |
* Copyright (c) 2017 Cesanta Software Limited |
||
8667 |
* All rights reserved |
||
8668 |
*/ |
||
8669 |
|||
8670 |
/* Amalgamated: #include "common/cs_varint.h" */ |
||
8671 |
/* Amalgamated: #include "common/str_util.h" */ |
||
8672 |
|||
8673 |
/* Amalgamated: #include "mjs/src/mjs_bcode.h" */ |
||
8674 |
/* Amalgamated: #include "mjs/src/mjs_builtin.h" */ |
||
8675 |
/* Amalgamated: #include "mjs/src/mjs_core.h" */ |
||
8676 |
/* Amalgamated: #include "mjs/src/mjs_exec.h" */ |
||
8677 |
/* Amalgamated: #include "mjs/src/mjs_ffi.h" */ |
||
8678 |
/* Amalgamated: #include "mjs/src/mjs_internal.h" */ |
||
8679 |
/* Amalgamated: #include "mjs/src/mjs_license.h" */ |
||
8680 |
/* Amalgamated: #include "mjs/src/mjs_object.h" */ |
||
8681 |
/* Amalgamated: #include "mjs/src/mjs_primitive.h" */ |
||
8682 |
/* Amalgamated: #include "mjs/src/mjs_string.h" */ |
||
8683 |
/* Amalgamated: #include "mjs/src/mjs_util.h" */ |
||
8684 |
|||
8685 |
#ifndef MJS_OBJECT_ARENA_SIZE |
||
8686 |
#define MJS_OBJECT_ARENA_SIZE 20 |
||
8687 |
#endif |
||
8688 |
#ifndef MJS_PROPERTY_ARENA_SIZE |
||
8689 |
#define MJS_PROPERTY_ARENA_SIZE 20 |
||
8690 |
#endif |
||
8691 |
#ifndef MJS_FUNC_FFI_ARENA_SIZE |
||
8692 |
#define MJS_FUNC_FFI_ARENA_SIZE 20 |
||
8693 |
#endif |
||
8694 |
|||
8695 |
#ifndef MJS_OBJECT_ARENA_INC_SIZE |
||
8696 |
#define MJS_OBJECT_ARENA_INC_SIZE 10 |
||
8697 |
#endif |
||
8698 |
#ifndef MJS_PROPERTY_ARENA_INC_SIZE |
||
8699 |
#define MJS_PROPERTY_ARENA_INC_SIZE 10 |
||
8700 |
#endif |
||
8701 |
#ifndef MJS_FUNC_FFI_ARENA_INC_SIZE |
||
8702 |
#define MJS_FUNC_FFI_ARENA_INC_SIZE 10 |
||
8703 |
#endif |
||
8704 |
|||
8705 |
78 |
void mjs_destroy(struct mjs *mjs) { |
|
8706 |
{ |
||
8707 |
78 |
int parts_cnt = mjs_bcode_parts_cnt(mjs); |
|
8708 |
int i; |
||
8709 |
✓✓ | 144 |
for (i = 0; i < parts_cnt; i++) { |
8710 |
66 |
struct mjs_bcode_part *bp = mjs_bcode_part_get(mjs, i); |
|
8711 |
✓✗ | 66 |
if (!bp->in_rom) { |
8712 |
66 |
free((void *) bp->data.p); |
|
8713 |
} |
||
8714 |
} |
||
8715 |
} |
||
8716 |
|||
8717 |
78 |
mbuf_free(&mjs->bcode_gen); |
|
8718 |
78 |
mbuf_free(&mjs->bcode_parts); |
|
8719 |
78 |
mbuf_free(&mjs->stack); |
|
8720 |
78 |
mbuf_free(&mjs->call_stack); |
|
8721 |
78 |
mbuf_free(&mjs->arg_stack); |
|
8722 |
78 |
mbuf_free(&mjs->owned_strings); |
|
8723 |
78 |
mbuf_free(&mjs->foreign_strings); |
|
8724 |
78 |
mbuf_free(&mjs->owned_values); |
|
8725 |
78 |
mbuf_free(&mjs->scopes); |
|
8726 |
78 |
mbuf_free(&mjs->loop_addresses); |
|
8727 |
78 |
mbuf_free(&mjs->json_visited_stack); |
|
8728 |
78 |
free(mjs->error_msg); |
|
8729 |
78 |
free(mjs->stack_trace); |
|
8730 |
78 |
mjs_ffi_args_free_list(mjs); |
|
8731 |
78 |
gc_arena_destroy(mjs, &mjs->object_arena); |
|
8732 |
78 |
gc_arena_destroy(mjs, &mjs->property_arena); |
|
8733 |
78 |
gc_arena_destroy(mjs, &mjs->ffi_sig_arena); |
|
8734 |
78 |
free(mjs); |
|
8735 |
78 |
} |
|
8736 |
|||
8737 |
78 |
struct mjs *mjs_create(void) { |
|
8738 |
mjs_val_t global_object; |
||
8739 |
78 |
struct mjs *mjs = calloc(1, sizeof(*mjs)); |
|
8740 |
78 |
mbuf_init(&mjs->stack, 0); |
|
8741 |
78 |
mbuf_init(&mjs->call_stack, 0); |
|
8742 |
78 |
mbuf_init(&mjs->arg_stack, 0); |
|
8743 |
78 |
mbuf_init(&mjs->owned_strings, 0); |
|
8744 |
78 |
mbuf_init(&mjs->foreign_strings, 0); |
|
8745 |
78 |
mbuf_init(&mjs->bcode_gen, 0); |
|
8746 |
78 |
mbuf_init(&mjs->bcode_parts, 0); |
|
8747 |
78 |
mbuf_init(&mjs->owned_values, 0); |
|
8748 |
78 |
mbuf_init(&mjs->scopes, 0); |
|
8749 |
78 |
mbuf_init(&mjs->loop_addresses, 0); |
|
8750 |
78 |
mbuf_init(&mjs->json_visited_stack, 0); |
|
8751 |
|||
8752 |
78 |
mjs->bcode_len = 0; |
|
8753 |
|||
8754 |
/* |
||
8755 |
* The compacting GC exploits the null terminator of the previous string as a |
||
8756 |
* marker. |
||
8757 |
*/ |
||
8758 |
{ |
||
8759 |
78 |
char z = 0; |
|
8760 |
78 |
mbuf_append(&mjs->owned_strings, &z, 1); |
|
8761 |
} |
||
8762 |
|||
8763 |
78 |
gc_arena_init(&mjs->object_arena, sizeof(struct mjs_object), |
|
8764 |
MJS_OBJECT_ARENA_SIZE, MJS_OBJECT_ARENA_INC_SIZE); |
||
8765 |
78 |
gc_arena_init(&mjs->property_arena, sizeof(struct mjs_property), |
|
8766 |
MJS_PROPERTY_ARENA_SIZE, MJS_PROPERTY_ARENA_INC_SIZE); |
||
8767 |
78 |
gc_arena_init(&mjs->ffi_sig_arena, sizeof(struct mjs_ffi_sig), |
|
8768 |
MJS_FUNC_FFI_ARENA_SIZE, MJS_FUNC_FFI_ARENA_INC_SIZE); |
||
8769 |
78 |
mjs->ffi_sig_arena.destructor = mjs_ffi_sig_destructor; |
|
8770 |
|||
8771 |
78 |
global_object = mjs_mk_object(mjs); |
|
8772 |
78 |
mjs_init_builtin(mjs, global_object); |
|
8773 |
78 |
mjs_set_ffi_resolver(mjs, dlsym); |
|
8774 |
78 |
push_mjs_val(&mjs->scopes, global_object); |
|
8775 |
78 |
mjs->vals.this_obj = MJS_UNDEFINED; |
|
8776 |
78 |
mjs->vals.dataview_proto = MJS_UNDEFINED; |
|
8777 |
|||
8778 |
78 |
return mjs; |
|
8779 |
} |
||
8780 |
|||
8781 |
124 |
mjs_err_t mjs_set_errorf(struct mjs *mjs, mjs_err_t err, const char *fmt, ...) { |
|
8782 |
va_list ap; |
||
8783 |
124 |
va_start(ap, fmt); |
|
8784 |
124 |
free(mjs->error_msg); |
|
8785 |
124 |
mjs->error_msg = NULL; |
|
8786 |
124 |
mjs->error = err; |
|
8787 |
✓✓ | 124 |
if (fmt != NULL) { |
8788 |
58 |
mg_avprintf(&mjs->error_msg, 0, fmt, ap); |
|
8789 |
} |
||
8790 |
124 |
va_end(ap); |
|
8791 |
124 |
return err; |
|
8792 |
} |
||
8793 |
|||
8794 |
4 |
mjs_err_t mjs_prepend_errorf(struct mjs *mjs, mjs_err_t err, const char *fmt, |
|
8795 |
...) { |
||
8796 |
4 |
char *old_error_msg = mjs->error_msg; |
|
8797 |
4 |
char *new_error_msg = NULL; |
|
8798 |
va_list ap; |
||
8799 |
4 |
va_start(ap, fmt); |
|
8800 |
|||
8801 |
/* err should never be MJS_OK here */ |
||
8802 |
✗✓ | 4 |
assert(err != MJS_OK); |
8803 |
|||
8804 |
4 |
mjs->error_msg = NULL; |
|
8805 |
/* set error if only it wasn't already set to some error */ |
||
8806 |
✓✗ | 4 |
if (mjs->error == MJS_OK) { |
8807 |
4 |
mjs->error = err; |
|
8808 |
} |
||
8809 |
4 |
mg_avprintf(&new_error_msg, 0, fmt, ap); |
|
8810 |
4 |
va_end(ap); |
|
8811 |
|||
8812 |
✗✓ | 4 |
if (old_error_msg != NULL) { |
8813 |
mg_asprintf(&mjs->error_msg, 0, "%s: %s", new_error_msg, old_error_msg); |
||
8814 |
free(new_error_msg); |
||
8815 |
free(old_error_msg); |
||
8816 |
} else { |
||
8817 |
4 |
mjs->error_msg = new_error_msg; |
|
8818 |
} |
||
8819 |
4 |
return err; |
|
8820 |
} |
||
8821 |
|||
8822 |
12 |
void mjs_print_error(struct mjs *mjs, FILE *fp, const char *msg, |
|
8823 |
int print_stack_trace) { |
||
8824 |
✓✗✗✓ |
12 |
if (print_stack_trace && mjs->stack_trace != NULL) { |
8825 |
fprintf(fp, "%s", mjs->stack_trace); |
||
8826 |
} |
||
8827 |
|||
8828 |
✓✗ | 12 |
if (msg == NULL) { |
8829 |
12 |
msg = "MJS error"; |
|
8830 |
} |
||
8831 |
|||
8832 |
12 |
fprintf(fp, "%s: %s\n", msg, mjs_strerror(mjs, mjs->error)); |
|
8833 |
12 |
} |
|
8834 |
|||
8835 |
MJS_PRIVATE void mjs_die(struct mjs *mjs) { |
||
8836 |
mjs_val_t msg_v = MJS_UNDEFINED; |
||
8837 |
const char *msg = NULL; |
||
8838 |
size_t msg_len = 0; |
||
8839 |
|||
8840 |
/* get idx from arg 0 */ |
||
8841 |
if (!mjs_check_arg(mjs, 0, "msg", MJS_TYPE_STRING, &msg_v)) { |
||
8842 |
goto clean; |
||
8843 |
} |
||
8844 |
|||
8845 |
msg = mjs_get_string(mjs, &msg_v, &msg_len); |
||
8846 |
|||
8847 |
/* TODO(dfrank): take error type as an argument */ |
||
8848 |
mjs_prepend_errorf(mjs, MJS_TYPE_ERROR, "%.*s", (int) msg_len, msg); |
||
8849 |
|||
8850 |
clean: |
||
8851 |
mjs_return(mjs, MJS_UNDEFINED); |
||
8852 |
} |
||
8853 |
|||
8854 |
12 |
const char *mjs_strerror(struct mjs *mjs, enum mjs_err err) { |
|
8855 |
12 |
const char *err_names[] = { |
|
8856 |
"NO_ERROR", "SYNTAX_ERROR", "REFERENCE_ERROR", |
||
8857 |
"TYPE_ERROR", "OUT_OF_MEMORY", "INTERNAL_ERROR", |
||
8858 |
"NOT_IMPLEMENTED", "FILE_OPEN_ERROR", "BAD_ARGUMENTS"}; |
||
8859 |
✗✓ | 36 |
return mjs->error_msg == NULL || mjs->error_msg[0] == '\0' ? err_names[err] |
8860 |
✓✗ | 12 |
: mjs->error_msg; |
8861 |
} |
||
8862 |
|||
8863 |
MJS_PRIVATE size_t mjs_get_func_addr(mjs_val_t v) { |
||
8864 |
return v & ~MJS_TAG_MASK; |
||
8865 |
} |
||
8866 |
|||
8867 |
1 |
MJS_PRIVATE enum mjs_type mjs_get_type(mjs_val_t v) { |
|
8868 |
int tag; |
||
8869 |
✗✓ | 1 |
if (mjs_is_number(v)) { |
8870 |
return MJS_TYPE_NUMBER; |
||
8871 |
} |
||
8872 |
1 |
tag = (v & MJS_TAG_MASK) >> 48; |
|
8873 |
✗✗✗✗ ✗✓✗✗ ✗ |
1 |
switch (tag) { |
8874 |
case MJS_TAG_FOREIGN >> 48: |
||
8875 |
return MJS_TYPE_FOREIGN; |
||
8876 |
case MJS_TAG_UNDEFINED >> 48: |
||
8877 |
return MJS_TYPE_UNDEFINED; |
||
8878 |
case MJS_TAG_OBJECT >> 48: |
||
8879 |
return MJS_TYPE_OBJECT_GENERIC; |
||
8880 |
case MJS_TAG_ARRAY >> 48: |
||
8881 |
return MJS_TYPE_OBJECT_ARRAY; |
||
8882 |
case MJS_TAG_FUNCTION >> 48: |
||
8883 |
return MJS_TYPE_OBJECT_FUNCTION; |
||
8884 |
case MJS_TAG_STRING_I >> 48: |
||
8885 |
case MJS_TAG_STRING_O >> 48: |
||
8886 |
case MJS_TAG_STRING_F >> 48: |
||
8887 |
case MJS_TAG_STRING_D >> 48: |
||
8888 |
case MJS_TAG_STRING_5 >> 48: |
||
8889 |
1 |
return MJS_TYPE_STRING; |
|
8890 |
case MJS_TAG_BOOLEAN >> 48: |
||
8891 |
return MJS_TYPE_BOOLEAN; |
||
8892 |
case MJS_TAG_NULL >> 48: |
||
8893 |
return MJS_TYPE_NULL; |
||
8894 |
default: |
||
8895 |
abort(); |
||
8896 |
return MJS_TYPE_UNDEFINED; |
||
8897 |
} |
||
8898 |
} |
||
8899 |
|||
8900 |
mjs_val_t mjs_get_global(struct mjs *mjs) { |
||
8901 |
return *vptr(&mjs->scopes, 0); |
||
8902 |
} |
||
8903 |
|||
8904 |
50 |
static void mjs_append_stack_trace_line(struct mjs *mjs, size_t offset) { |
|
8905 |
✓✗ | 50 |
if (offset != MJS_BCODE_OFFSET_EXIT) { |
8906 |
50 |
const char *filename = mjs_get_bcode_filename_by_offset(mjs, offset); |
|
8907 |
50 |
int line_no = mjs_get_lineno_by_offset(mjs, offset); |
|
8908 |
50 |
char *new_line = NULL; |
|
8909 |
50 |
const char *fmt = " at %s:%d\n"; |
|
8910 |
✗✓ | 50 |
if (filename == NULL) { |
8911 |
fprintf(stderr, |
||
8912 |
"ERROR during stack trace generation: wrong bcode offset %d\n", |
||
8913 |
(int) offset); |
||
8914 |
filename = "<unknown-filename>"; |
||
8915 |
} |
||
8916 |
50 |
mg_asprintf(&new_line, 0, fmt, filename, line_no); |
|
8917 |
|||
8918 |
✗✓ | 50 |
if (mjs->stack_trace != NULL) { |
8919 |
char *old = mjs->stack_trace; |
||
8920 |
mg_asprintf(&mjs->stack_trace, 0, "%s%s", mjs->stack_trace, new_line); |
||
8921 |
free(old); |
||
8922 |
free(new_line); |
||
8923 |
} else { |
||
8924 |
50 |
mjs->stack_trace = new_line; |
|
8925 |
} |
||
8926 |
} |
||
8927 |
50 |
} |
|
8928 |
|||
8929 |
50 |
MJS_PRIVATE void mjs_gen_stack_trace(struct mjs *mjs, size_t offset) { |
|
8930 |
50 |
mjs_append_stack_trace_line(mjs, offset); |
|
8931 |
✗✓ | 100 |
while (mjs->call_stack.len >= |
8932 |
sizeof(mjs_val_t) * CALL_STACK_FRAME_ITEMS_CNT) { |
||
8933 |
int i; |
||
8934 |
|||
8935 |
/* set current offset to it to the offset stored in the frame */ |
||
8936 |
offset = mjs_get_int( |
||
8937 |
mjs, *vptr(&mjs->call_stack, -1 - CALL_STACK_FRAME_ITEM_RETURN_ADDR)); |
||
8938 |
|||
8939 |
/* pop frame from the call stack */ |
||
8940 |
for (i = 0; i < CALL_STACK_FRAME_ITEMS_CNT; i++) { |
||
8941 |
mjs_pop_val(&mjs->call_stack); |
||
8942 |
} |
||
8943 |
|||
8944 |
mjs_append_stack_trace_line(mjs, offset); |
||
8945 |
} |
||
8946 |
50 |
} |
|
8947 |
|||
8948 |
void mjs_own(struct mjs *mjs, mjs_val_t *v) { |
||
8949 |
mbuf_append(&mjs->owned_values, &v, sizeof(v)); |
||
8950 |
} |
||
8951 |
|||
8952 |
int mjs_disown(struct mjs *mjs, mjs_val_t *v) { |
||
8953 |
mjs_val_t **vp = (mjs_val_t **) (mjs->owned_values.buf + |
||
8954 |
mjs->owned_values.len - sizeof(v)); |
||
8955 |
|||
8956 |
for (; (char *) vp >= mjs->owned_values.buf; vp--) { |
||
8957 |
if (*vp == v) { |
||
8958 |
*vp = *(mjs_val_t **) (mjs->owned_values.buf + mjs->owned_values.len - |
||
8959 |
sizeof(v)); |
||
8960 |
mjs->owned_values.len -= sizeof(v); |
||
8961 |
return 1; |
||
8962 |
} |
||
8963 |
} |
||
8964 |
|||
8965 |
return 0; |
||
8966 |
} |
||
8967 |
|||
8968 |
/* |
||
8969 |
* Returns position in the data stack at which the called function is located, |
||
8970 |
* and which should be later replaced with the returned value. |
||
8971 |
*/ |
||
8972 |
MJS_PRIVATE int mjs_getretvalpos(struct mjs *mjs) { |
||
8973 |
int pos; |
||
8974 |
mjs_val_t *ppos = vptr(&mjs->call_stack, -1); |
||
8975 |
// LOG(LL_INFO, ("ppos: %p %d", ppos, mjs_stack_size(&mjs->call_stack))); |
||
8976 |
assert(ppos != NULL && mjs_is_number(*ppos)); |
||
8977 |
pos = mjs_get_int(mjs, *ppos) - 1; |
||
8978 |
assert(pos < (int) mjs_stack_size(&mjs->stack)); |
||
8979 |
return pos; |
||
8980 |
} |
||
8981 |
|||
8982 |
int mjs_nargs(struct mjs *mjs) { |
||
8983 |
int top = mjs_stack_size(&mjs->stack); |
||
8984 |
int pos = mjs_getretvalpos(mjs) + 1; |
||
8985 |
// LOG(LL_INFO, ("top: %d pos: %d", top, pos)); |
||
8986 |
return pos > 0 && pos < top ? top - pos : 0; |
||
8987 |
} |
||
8988 |
|||
8989 |
mjs_val_t mjs_arg(struct mjs *mjs, int arg_index) { |
||
8990 |
mjs_val_t res = MJS_UNDEFINED; |
||
8991 |
int top = mjs_stack_size(&mjs->stack); |
||
8992 |
int pos = mjs_getretvalpos(mjs) + 1; |
||
8993 |
// LOG(LL_INFO, ("idx %d pos: %d", arg_index, pos)); |
||
8994 |
if (pos > 0 && pos + arg_index < top) { |
||
8995 |
res = *vptr(&mjs->stack, pos + arg_index); |
||
8996 |
} |
||
8997 |
return res; |
||
8998 |
} |
||
8999 |
|||
9000 |
void mjs_return(struct mjs *mjs, mjs_val_t v) { |
||
9001 |
int pos = mjs_getretvalpos(mjs); |
||
9002 |
// LOG(LL_INFO, ("pos: %d", pos)); |
||
9003 |
mjs->stack.len = sizeof(mjs_val_t) * pos; |
||
9004 |
mjs_push(mjs, v); |
||
9005 |
} |
||
9006 |
|||
9007 |
41 |
MJS_PRIVATE mjs_val_t vtop(struct mbuf *m) { |
|
9008 |
41 |
size_t size = mjs_stack_size(m); |
|
9009 |
✓✗ | 41 |
return size > 0 ? *vptr(m, size - 1) : MJS_UNDEFINED; |
9010 |
} |
||
9011 |
|||
9012 |
167 |
MJS_PRIVATE size_t mjs_stack_size(const struct mbuf *m) { |
|
9013 |
167 |
return m->len / sizeof(mjs_val_t); |
|
9014 |
} |
||
9015 |
|||
9016 |
81 |
MJS_PRIVATE mjs_val_t *vptr(struct mbuf *m, int idx) { |
|
9017 |
81 |
int size = mjs_stack_size(m); |
|
9018 |
✗✓ | 81 |
if (idx < 0) idx = size + idx; |
9019 |
✓✗✓✗ |
81 |
return idx >= 0 && idx < size ? &((mjs_val_t *) m->buf)[idx] : NULL; |
9020 |
} |
||
9021 |
|||
9022 |
105 |
MJS_PRIVATE mjs_val_t mjs_pop(struct mjs *mjs) { |
|
9023 |
✗✓ | 105 |
if (mjs->stack.len == 0) { |
9024 |
mjs_set_errorf(mjs, MJS_INTERNAL_ERROR, "stack underflow"); |
||
9025 |
return MJS_UNDEFINED; |
||
9026 |
} else { |
||
9027 |
105 |
return mjs_pop_val(&mjs->stack); |
|
9028 |
} |
||
9029 |
} |
||
9030 |
|||
9031 |
273 |
MJS_PRIVATE void push_mjs_val(struct mbuf *m, mjs_val_t v) { |
|
9032 |
273 |
mbuf_append(m, &v, sizeof(v)); |
|
9033 |
273 |
} |
|
9034 |
|||
9035 |
105 |
MJS_PRIVATE mjs_val_t mjs_pop_val(struct mbuf *m) { |
|
9036 |
105 |
mjs_val_t v = MJS_UNDEFINED; |
|
9037 |
✗✓ | 105 |
assert(m->len >= sizeof(v)); |
9038 |
✓✗ | 105 |
if (m->len >= sizeof(v)) { |
9039 |
105 |
memcpy(&v, m->buf + m->len - sizeof(v), sizeof(v)); |
|
9040 |
105 |
m->len -= sizeof(v); |
|
9041 |
} |
||
9042 |
105 |
return v; |
|
9043 |
} |
||
9044 |
|||
9045 |
195 |
MJS_PRIVATE void mjs_push(struct mjs *mjs, mjs_val_t v) { |
|
9046 |
195 |
push_mjs_val(&mjs->stack, v); |
|
9047 |
195 |
} |
|
9048 |
|||
9049 |
void mjs_set_generate_jsc(struct mjs *mjs, int generate_jsc) { |
||
9050 |
mjs->generate_jsc = generate_jsc; |
||
9051 |
} |
||
9052 |
#ifdef MJS_MODULE_LINES |
||
9053 |
#line 1 "mjs/src/mjs_dataview.c" |
||
9054 |
#endif |
||
9055 |
/* |
||
9056 |
* Copyright (c) 2017 Cesanta Software Limited |
||
9057 |
* All rights reserved |
||
9058 |
*/ |
||
9059 |
|||
9060 |
/* Amalgamated: #include "mjs/src/mjs_exec_public.h" */ |
||
9061 |
/* Amalgamated: #include "mjs/src/mjs_internal.h" */ |
||
9062 |
/* Amalgamated: #include "mjs/src/mjs_object.h" */ |
||
9063 |
/* Amalgamated: #include "mjs/src/mjs_primitive.h" */ |
||
9064 |
/* Amalgamated: #include "mjs/src/mjs_util.h" */ |
||
9065 |
|||
9066 |
void *mjs_mem_to_ptr(unsigned val) { |
||
9067 |
return (void *) (uintptr_t) val; |
||
9068 |
} |
||
9069 |
|||
9070 |
void *mjs_mem_get_ptr(void *base, int offset) { |
||
9071 |
return (char *) base + offset; |
||
9072 |
} |
||
9073 |
|||
9074 |
void mjs_mem_set_ptr(void *ptr, void *val) { |
||
9075 |
*(void **) ptr = val; |
||
9076 |
} |
||
9077 |
|||
9078 |
double mjs_mem_get_dbl(void *ptr) { |
||
9079 |
double v; |
||
9080 |
memcpy(&v, ptr, sizeof(v)); |
||
9081 |
return v; |
||
9082 |
} |
||
9083 |
|||
9084 |
void mjs_mem_set_dbl(void *ptr, double val) { |
||
9085 |
memcpy(ptr, &val, sizeof(val)); |
||
9086 |
} |
||
9087 |
|||
9088 |
/* |
||
9089 |
* TODO(dfrank): add support for unsigned ints to ffi and use |
||
9090 |
* unsigned int here |
||
9091 |
*/ |
||
9092 |
double mjs_mem_get_uint(void *ptr, int size, int bigendian) { |
||
9093 |
uint8_t *p = (uint8_t *) ptr; |
||
9094 |
int i, inc = bigendian ? 1 : -1; |
||
9095 |
unsigned int res = 0; |
||
9096 |
p += bigendian ? 0 : size - 1; |
||
9097 |
for (i = 0; i < size; i++, p += inc) { |
||
9098 |
res <<= 8; |
||
9099 |
res |= *p; |
||
9100 |
} |
||
9101 |
return res; |
||
9102 |
} |
||
9103 |
|||
9104 |
/* |
||
9105 |
* TODO(dfrank): add support for unsigned ints to ffi and use |
||
9106 |
* unsigned int here |
||
9107 |
*/ |
||
9108 |
double mjs_mem_get_int(void *ptr, int size, int bigendian) { |
||
9109 |
uint8_t *p = (uint8_t *) ptr; |
||
9110 |
int i, inc = bigendian ? 1 : -1; |
||
9111 |
int res = 0; |
||
9112 |
p += bigendian ? 0 : size - 1; |
||
9113 |
|||
9114 |
for (i = 0; i < size; i++, p += inc) { |
||
9115 |
res <<= 8; |
||
9116 |
res |= *p; |
||
9117 |
} |
||
9118 |
|||
9119 |
/* sign-extend */ |
||
9120 |
{ |
||
9121 |
int extra = sizeof(res) - size; |
||
9122 |
for (i = 0; i < extra; i++) res <<= 8; |
||
9123 |
for (i = 0; i < extra; i++) res >>= 8; |
||
9124 |
} |
||
9125 |
|||
9126 |
return res; |
||
9127 |
} |
||
9128 |
|||
9129 |
void mjs_mem_set_uint(void *ptr, unsigned int val, int size, int bigendian) { |
||
9130 |
uint8_t *p = (uint8_t *) ptr + (bigendian ? size - 1 : 0); |
||
9131 |
int i, inc = bigendian ? -1 : 1; |
||
9132 |
for (i = 0; i < size; i++, p += inc) { |
||
9133 |
*p = val & 0xff; |
||
9134 |
val >>= 8; |
||
9135 |
} |
||
9136 |
} |
||
9137 |
|||
9138 |
void mjs_mem_set_int(void *ptr, int val, int size, int bigendian) { |
||
9139 |
mjs_mem_set_uint(ptr, val, size, bigendian); |
||
9140 |
} |
||
9141 |
#ifdef MJS_MODULE_LINES |
||
9142 |
#line 1 "mjs/src/mjs_exec.c" |
||
9143 |
#endif |
||
9144 |
/* |
||
9145 |
* Copyright (c) 2017 Cesanta Software Limited |
||
9146 |
* All rights reserved |
||
9147 |
*/ |
||
9148 |
|||
9149 |
/* Amalgamated: #include "common/cs_file.h" */ |
||
9150 |
/* Amalgamated: #include "common/cs_varint.h" */ |
||
9151 |
|||
9152 |
/* Amalgamated: #include "mjs/src/mjs_array.h" */ |
||
9153 |
/* Amalgamated: #include "mjs/src/mjs_bcode.h" */ |
||
9154 |
/* Amalgamated: #include "mjs/src/mjs_conversion.h" */ |
||
9155 |
/* Amalgamated: #include "mjs/src/mjs_core.h" */ |
||
9156 |
/* Amalgamated: #include "mjs/src/mjs_exec.h" */ |
||
9157 |
/* Amalgamated: #include "mjs/src/mjs_internal.h" */ |
||
9158 |
/* Amalgamated: #include "mjs/src/mjs_object.h" */ |
||
9159 |
/* Amalgamated: #include "mjs/src/mjs_parser.h" */ |
||
9160 |
/* Amalgamated: #include "mjs/src/mjs_primitive.h" */ |
||
9161 |
/* Amalgamated: #include "mjs/src/mjs_string.h" */ |
||
9162 |
/* Amalgamated: #include "mjs/src/mjs_tok.h" */ |
||
9163 |
/* Amalgamated: #include "mjs/src/mjs_util.h" */ |
||
9164 |
|||
9165 |
#if MJS_GENERATE_JSC && defined(CS_MMAP) |
||
9166 |
#include <sys/mman.h> |
||
9167 |
#endif |
||
9168 |
|||
9169 |
/* |
||
9170 |
* Pushes call stack frame. Offset is a global bcode offset. Retval_stack_idx |
||
9171 |
* is an index in mjs->stack at which return value should be written later. |
||
9172 |
*/ |
||
9173 |
static void call_stack_push_frame(struct mjs *mjs, size_t offset, |
||
9174 |
mjs_val_t retval_stack_idx) { |
||
9175 |
/* Pop `this` value, and apply it */ |
||
9176 |
mjs_val_t this_obj = mjs_pop_val(&mjs->arg_stack); |
||
9177 |
|||
9178 |
/* |
||
9179 |
* NOTE: the layout is described by enum mjs_call_stack_frame_item |
||
9180 |
*/ |
||
9181 |
push_mjs_val(&mjs->call_stack, mjs->vals.this_obj); |
||
9182 |
mjs->vals.this_obj = this_obj; |
||
9183 |
|||
9184 |
push_mjs_val(&mjs->call_stack, mjs_mk_number(mjs, (double) offset)); |
||
9185 |
push_mjs_val(&mjs->call_stack, |
||
9186 |
mjs_mk_number(mjs, (double) mjs_stack_size(&mjs->scopes))); |
||
9187 |
push_mjs_val( |
||
9188 |
&mjs->call_stack, |
||
9189 |
mjs_mk_number(mjs, (double) mjs_stack_size(&mjs->loop_addresses))); |
||
9190 |
push_mjs_val(&mjs->call_stack, retval_stack_idx); |
||
9191 |
} |
||
9192 |
|||
9193 |
/* |
||
9194 |
* Restores call stack frame. Returns the return address. |
||
9195 |
*/ |
||
9196 |
static size_t call_stack_restore_frame(struct mjs *mjs) { |
||
9197 |
size_t retval_stack_idx, return_address, scope_index, loop_addr_index; |
||
9198 |
assert(mjs_stack_size(&mjs->call_stack) >= CALL_STACK_FRAME_ITEMS_CNT); |
||
9199 |
|||
9200 |
/* |
||
9201 |
* NOTE: the layout is described by enum mjs_call_stack_frame_item |
||
9202 |
*/ |
||
9203 |
retval_stack_idx = mjs_get_int(mjs, mjs_pop_val(&mjs->call_stack)); |
||
9204 |
loop_addr_index = mjs_get_int(mjs, mjs_pop_val(&mjs->call_stack)); |
||
9205 |
scope_index = mjs_get_int(mjs, mjs_pop_val(&mjs->call_stack)); |
||
9206 |
return_address = mjs_get_int(mjs, mjs_pop_val(&mjs->call_stack)); |
||
9207 |
mjs->vals.this_obj = mjs_pop_val(&mjs->call_stack); |
||
9208 |
|||
9209 |
/* Remove created scopes */ |
||
9210 |
while (mjs_stack_size(&mjs->scopes) > scope_index) { |
||
9211 |
mjs_pop_val(&mjs->scopes); |
||
9212 |
} |
||
9213 |
|||
9214 |
/* Remove loop addresses */ |
||
9215 |
while (mjs_stack_size(&mjs->loop_addresses) > loop_addr_index) { |
||
9216 |
mjs_pop_val(&mjs->loop_addresses); |
||
9217 |
} |
||
9218 |
|||
9219 |
/* Shrink stack, leave return value on top */ |
||
9220 |
mjs->stack.len = retval_stack_idx * sizeof(mjs_val_t); |
||
9221 |
|||
9222 |
/* Jump to the return address */ |
||
9223 |
return return_address; |
||
9224 |
} |
||
9225 |
|||
9226 |
40 |
static mjs_val_t mjs_find_scope(struct mjs *mjs, mjs_val_t key) { |
|
9227 |
40 |
size_t num_scopes = mjs_stack_size(&mjs->scopes); |
|
9228 |
✓✓ | 40 |
while (num_scopes > 0) { |
9229 |
40 |
mjs_val_t scope = *vptr(&mjs->scopes, num_scopes - 1); |
|
9230 |
40 |
num_scopes--; |
|
9231 |
✗✓ | 40 |
if (mjs_get_own_property_v(mjs, scope, key) != NULL) return scope; |
9232 |
} |
||
9233 |
40 |
mjs_set_errorf(mjs, MJS_REFERENCE_ERROR, "[%s] is not defined", |
|
9234 |
mjs_get_cstring(mjs, &key)); |
||
9235 |
40 |
return MJS_UNDEFINED; |
|
9236 |
} |
||
9237 |
|||
9238 |
mjs_val_t mjs_get_this(struct mjs *mjs) { |
||
9239 |
return mjs->vals.this_obj; |
||
9240 |
} |
||
9241 |
|||
9242 |
4 |
static double do_arith_op(double da, double db, int op, bool *resnan) { |
|
9243 |
4 |
*resnan = false; |
|
9244 |
|||
9245 |
✓✗✗✓ |
4 |
if (isnan(da) || isnan(db)) { |
9246 |
*resnan = true; |
||
9247 |
return 0; |
||
9248 |
} |
||
9249 |
/* clang-format off */ |
||
9250 |
✗✗✗✓ ✗✗✓✗ ✗✗✗✗ |
4 |
switch (op) { |
9251 |
case TOK_MINUS: return da - db; |
||
9252 |
case TOK_PLUS: return da + db; |
||
9253 |
case TOK_MUL: return da * db; |
||
9254 |
case TOK_DIV: |
||
9255 |
✓✗ | 2 |
if (db != 0) { |
9256 |
2 |
return da / db; |
|
9257 |
} else { |
||
9258 |
/* TODO(dfrank): add support for Infinity and return it here */ |
||
9259 |
*resnan = true; |
||
9260 |
return 0; |
||
9261 |
} |
||
9262 |
case TOK_REM: |
||
9263 |
/* |
||
9264 |
* TODO(dfrank): probably support remainder operation as it is in JS |
||
9265 |
* (which works with non-integer divisor). |
||
9266 |
*/ |
||
9267 |
db = (int) db; |
||
9268 |
if (db != 0) { |
||
9269 |
bool neg = false; |
||
9270 |
if (da < 0) { |
||
9271 |
neg = true; |
||
9272 |
da = -da; |
||
9273 |
} |
||
9274 |
if (db < 0) { |
||
9275 |
db = -db; |
||
9276 |
} |
||
9277 |
da = (double) ((int64_t) da % (int64_t) db); |
||
9278 |
if (neg) { |
||
9279 |
da = -da; |
||
9280 |
} |
||
9281 |
return da; |
||
9282 |
} else { |
||
9283 |
*resnan = true; |
||
9284 |
return 0; |
||
9285 |
} |
||
9286 |
case TOK_AND: return (double) ((int64_t) da & (int64_t) db); |
||
9287 |
2 |
case TOK_OR: return (double) ((int64_t) da | (int64_t) db); |
|
9288 |
case TOK_XOR: return (double) ((int64_t) da ^ (int64_t) db); |
||
9289 |
case TOK_LSHIFT: return (double) ((int64_t) da << (int64_t) db); |
||
9290 |
case TOK_RSHIFT: return (double) ((int64_t) da >> (int64_t) db); |
||
9291 |
case TOK_URSHIFT: return (double) ((uint32_t) da >> (uint32_t) db); |
||
9292 |
} |
||
9293 |
/* clang-format on */ |
||
9294 |
*resnan = true; |
||
9295 |
return 0; |
||
9296 |
} |
||
9297 |
|||
9298 |
4 |
static void set_no_autoconversion_error(struct mjs *mjs) { |
|
9299 |
4 |
mjs_prepend_errorf(mjs, MJS_TYPE_ERROR, |
|
9300 |
"implicit type conversion is prohibited"); |
||
9301 |
4 |
} |
|
9302 |
|||
9303 |
8 |
static mjs_val_t do_op(struct mjs *mjs, mjs_val_t a, mjs_val_t b, int op) { |
|
9304 |
8 |
mjs_val_t ret = MJS_UNDEFINED; |
|
9305 |
8 |
bool resnan = false; |
|
9306 |
✓✗✓✓ ✓✗ |
15 |
if ((mjs_is_foreign(a) || mjs_is_number(a)) && |
9307 |
✓✓ | 18 |
(mjs_is_foreign(b) || mjs_is_number(b))) { |
9308 |
4 |
int is_result_ptr = 0; |
|
9309 |
double da, db, result; |
||
9310 |
|||
9311 |
✗✓✗✗ |
4 |
if (mjs_is_foreign(a) && mjs_is_foreign(b)) { |
9312 |
/* When two operands are pointers, only subtraction is supported */ |
||
9313 |
if (op != TOK_MINUS) { |
||
9314 |
mjs_prepend_errorf(mjs, MJS_TYPE_ERROR, "invalid operands"); |
||
9315 |
} |
||
9316 |
✓✗✗✓ |
4 |
} else if (mjs_is_foreign(a) || mjs_is_foreign(b)) { |
9317 |
/* |
||
9318 |
* When one of the operands is a pointer, only + and - are supported, |
||
9319 |
* and the result is a pointer. |
||
9320 |
*/ |
||
9321 |
if (op != TOK_MINUS && op != TOK_PLUS) { |
||
9322 |
mjs_prepend_errorf(mjs, MJS_TYPE_ERROR, "invalid operands"); |
||
9323 |
} |
||
9324 |
is_result_ptr = 1; |
||
9325 |
} |
||
9326 |
8 |
da = mjs_is_number(a) ? mjs_get_double(mjs, a) |
|
9327 |
✓✗ | 4 |
: (double) (uintptr_t) mjs_get_ptr(mjs, a); |
9328 |
8 |
db = mjs_is_number(b) ? mjs_get_double(mjs, b) |
|
9329 |
✓✗ | 4 |
: (double) (uintptr_t) mjs_get_ptr(mjs, b); |
9330 |
4 |
result = do_arith_op(da, db, op, &resnan); |
|
9331 |
✗✓ | 4 |
if (resnan) { |
9332 |
ret = MJS_TAG_NAN; |
||
9333 |
} else { |
||
9334 |
/* |
||
9335 |
* If at least one of the operands was a pointer, result should also be |
||
9336 |
* a pointer |
||
9337 |
*/ |
||
9338 |
4 |
ret = is_result_ptr ? mjs_mk_foreign(mjs, (void *) (uintptr_t) result) |
|
9339 |
✗✓ | 4 |
: mjs_mk_number(mjs, result); |
9340 |
} |
||
9341 |
✗✓✗✗ ✗✗ |
4 |
} else if (mjs_is_string(a) && mjs_is_string(b) && (op == TOK_PLUS)) { |
9342 |
ret = s_concat(mjs, a, b); |
||
9343 |
} else { |
||
9344 |
4 |
set_no_autoconversion_error(mjs); |
|
9345 |
} |
||
9346 |
8 |
return ret; |
|
9347 |
} |
||
9348 |
|||
9349 |
static void op_assign(struct mjs *mjs, int op) { |
||
9350 |
mjs_val_t val = mjs_pop(mjs); |
||
9351 |
mjs_val_t obj = mjs_pop(mjs); |
||
9352 |
mjs_val_t key = mjs_pop(mjs); |
||
9353 |
if (mjs_is_object(obj) && mjs_is_string(key)) { |
||
9354 |
mjs_val_t v = mjs_get_v(mjs, obj, key); |
||
9355 |
mjs_set_v(mjs, obj, key, do_op(mjs, v, val, op)); |
||
9356 |
mjs_push(mjs, v); |
||
9357 |
} else { |
||
9358 |
mjs_set_errorf(mjs, MJS_TYPE_ERROR, "invalid operand"); |
||
9359 |
} |
||
9360 |
} |
||
9361 |
|||
9362 |
static int check_equal(struct mjs *mjs, mjs_val_t a, mjs_val_t b) { |
||
9363 |
int ret = 0; |
||
9364 |
if (a == MJS_TAG_NAN && b == MJS_TAG_NAN) { |
||
9365 |
ret = 0; |
||
9366 |
} else if (a == b) { |
||
9367 |
ret = 1; |
||
9368 |
} else if (mjs_is_number(a) && mjs_is_number(b)) { |
||
9369 |
/* |
||
9370 |
* The case of equal numbers is handled above, so here the result is always |
||
9371 |
* false |
||
9372 |
*/ |
||
9373 |
ret = 0; |
||
9374 |
} else if (mjs_is_string(a) && mjs_is_string(b)) { |
||
9375 |
ret = s_cmp(mjs, a, b) == 0; |
||
9376 |
} else if (mjs_is_foreign(a) && b == MJS_NULL) { |
||
9377 |
ret = mjs_get_ptr(mjs, a) == NULL; |
||
9378 |
} else if (a == MJS_NULL && mjs_is_foreign(b)) { |
||
9379 |
ret = mjs_get_ptr(mjs, b) == NULL; |
||
9380 |
} else { |
||
9381 |
ret = 0; |
||
9382 |
} |
||
9383 |
return ret; |
||
9384 |
} |
||
9385 |
|||
9386 |
13 |
static void exec_expr(struct mjs *mjs, int op) { |
|
9387 |
✗✓✓✗ ✗✗✗✗ ✗✗✗✗ ✗✗✗✗ ✗✓✗✗ ✗✗✗✗ ✗✗✗✗ ✗✗✗✓ ✗ |
13 |
switch (op) { |
9388 |
case TOK_DOT: |
||
9389 |
break; |
||
9390 |
case TOK_MINUS: |
||
9391 |
case TOK_PLUS: |
||
9392 |
case TOK_MUL: |
||
9393 |
case TOK_DIV: |
||
9394 |
case TOK_REM: |
||
9395 |
case TOK_XOR: |
||
9396 |
case TOK_AND: |
||
9397 |
case TOK_OR: |
||
9398 |
case TOK_LSHIFT: |
||
9399 |
case TOK_RSHIFT: |
||
9400 |
case TOK_URSHIFT: { |
||
9401 |
8 |
mjs_val_t b = mjs_pop(mjs); |
|
9402 |
8 |
mjs_val_t a = mjs_pop(mjs); |
|
9403 |
8 |
mjs_push(mjs, do_op(mjs, a, b, op)); |
|
9404 |
8 |
break; |
|
9405 |
} |
||
9406 |
case TOK_UNARY_MINUS: { |
||
9407 |
2 |
double a = mjs_get_double(mjs, mjs_pop(mjs)); |
|
9408 |
2 |
mjs_push(mjs, mjs_mk_number(mjs, -a)); |
|
9409 |
2 |
break; |
|
9410 |
} |
||
9411 |
case TOK_NOT: { |
||
9412 |
mjs_val_t val = mjs_pop(mjs); |
||
9413 |
mjs_push(mjs, mjs_mk_boolean(mjs, !mjs_is_truthy(mjs, val))); |
||
9414 |
break; |
||
9415 |
} |
||
9416 |
case TOK_TILDA: { |
||
9417 |
double a = mjs_get_double(mjs, mjs_pop(mjs)); |
||
9418 |
mjs_push(mjs, mjs_mk_number(mjs, (double) (~(int64_t) a))); |
||
9419 |
break; |
||
9420 |
} |
||
9421 |
case TOK_UNARY_PLUS: |
||
9422 |
break; |
||
9423 |
case TOK_EQ: |
||
9424 |
mjs_set_errorf(mjs, MJS_NOT_IMPLEMENTED_ERROR, "Use ===, not =="); |
||
9425 |
break; |
||
9426 |
case TOK_NE: |
||
9427 |
mjs_set_errorf(mjs, MJS_NOT_IMPLEMENTED_ERROR, "Use !==, not !="); |
||
9428 |
break; |
||
9429 |
case TOK_EQ_EQ: { |
||
9430 |
mjs_val_t a = mjs_pop(mjs); |
||
9431 |
mjs_val_t b = mjs_pop(mjs); |
||
9432 |
mjs_push(mjs, mjs_mk_boolean(mjs, check_equal(mjs, a, b))); |
||
9433 |
break; |
||
9434 |
} |
||
9435 |
case TOK_NE_NE: { |
||
9436 |
mjs_val_t a = mjs_pop(mjs); |
||
9437 |
mjs_val_t b = mjs_pop(mjs); |
||
9438 |
mjs_push(mjs, mjs_mk_boolean(mjs, !check_equal(mjs, a, b))); |
||
9439 |
break; |
||
9440 |
} |
||
9441 |
case TOK_LT: { |
||
9442 |
double b = mjs_get_double(mjs, mjs_pop(mjs)); |
||
9443 |
double a = mjs_get_double(mjs, mjs_pop(mjs)); |
||
9444 |
mjs_push(mjs, mjs_mk_boolean(mjs, a < b)); |
||
9445 |
break; |
||
9446 |
} |
||
9447 |
case TOK_GT: { |
||
9448 |
double b = mjs_get_double(mjs, mjs_pop(mjs)); |
||
9449 |
double a = mjs_get_double(mjs, mjs_pop(mjs)); |
||
9450 |
mjs_push(mjs, mjs_mk_boolean(mjs, a > b)); |
||
9451 |
break; |
||
9452 |
} |
||
9453 |
case TOK_LE: { |
||
9454 |
double b = mjs_get_double(mjs, mjs_pop(mjs)); |
||
9455 |
double a = mjs_get_double(mjs, mjs_pop(mjs)); |
||
9456 |
mjs_push(mjs, mjs_mk_boolean(mjs, a <= b)); |
||
9457 |
break; |
||
9458 |
} |
||
9459 |
case TOK_GE: { |
||
9460 |
double b = mjs_get_double(mjs, mjs_pop(mjs)); |
||
9461 |
double a = mjs_get_double(mjs, mjs_pop(mjs)); |
||
9462 |
mjs_push(mjs, mjs_mk_boolean(mjs, a >= b)); |
||
9463 |
break; |
||
9464 |
} |
||
9465 |
case TOK_ASSIGN: { |
||
9466 |
mjs_val_t val = mjs_pop(mjs); |
||
9467 |
mjs_val_t obj = mjs_pop(mjs); |
||
9468 |
mjs_val_t key = mjs_pop(mjs); |
||
9469 |
if (mjs_is_object(obj)) { |
||
9470 |
mjs_set_v(mjs, obj, key, val); |
||
9471 |
} else if (mjs_is_foreign(obj)) { |
||
9472 |
/* |
||
9473 |
* We don't have setters, so in order to support properties which behave |
||
9474 |
* like setters, we have to parse key right here, instead of having real |
||
9475 |
* built-in prototype objects |
||
9476 |
*/ |
||
9477 |
|||
9478 |
int ikey = mjs_get_int(mjs, key); |
||
9479 |
int ival = mjs_get_int(mjs, val); |
||
9480 |
|||
9481 |
if (!mjs_is_number(key)) { |
||
9482 |
mjs_prepend_errorf(mjs, MJS_TYPE_ERROR, "index must be a number"); |
||
9483 |
val = MJS_UNDEFINED; |
||
9484 |
} else if (!mjs_is_number(val) || ival < 0 || ival > 0xff) { |
||
9485 |
mjs_prepend_errorf(mjs, MJS_TYPE_ERROR, |
||
9486 |
"only number 0 .. 255 can be assigned"); |
||
9487 |
val = MJS_UNDEFINED; |
||
9488 |
} else { |
||
9489 |
uint8_t *ptr = (uint8_t *) mjs_get_ptr(mjs, obj); |
||
9490 |
*(ptr + ikey) = (uint8_t) ival; |
||
9491 |
} |
||
9492 |
} else { |
||
9493 |
mjs_prepend_errorf(mjs, MJS_TYPE_ERROR, "unsupported object type"); |
||
9494 |
} |
||
9495 |
mjs_push(mjs, val); |
||
9496 |
break; |
||
9497 |
} |
||
9498 |
case TOK_POSTFIX_PLUS: { |
||
9499 |
mjs_val_t obj = mjs_pop(mjs); |
||
9500 |
mjs_val_t key = mjs_pop(mjs); |
||
9501 |
if (mjs_is_object(obj) && mjs_is_string(key)) { |
||
9502 |
mjs_val_t v = mjs_get_v(mjs, obj, key); |
||
9503 |
mjs_val_t v1 = do_op(mjs, v, mjs_mk_number(mjs, 1), TOK_PLUS); |
||
9504 |
mjs_set_v(mjs, obj, key, v1); |
||
9505 |
mjs_push(mjs, v); |
||
9506 |
} else { |
||
9507 |
mjs_set_errorf(mjs, MJS_TYPE_ERROR, "invalid operand for ++"); |
||
9508 |
} |
||
9509 |
break; |
||
9510 |
} |
||
9511 |
case TOK_POSTFIX_MINUS: { |
||
9512 |
mjs_val_t obj = mjs_pop(mjs); |
||
9513 |
mjs_val_t key = mjs_pop(mjs); |
||
9514 |
if (mjs_is_object(obj) && mjs_is_string(key)) { |
||
9515 |
mjs_val_t v = mjs_get_v(mjs, obj, key); |
||
9516 |
mjs_val_t v1 = do_op(mjs, v, mjs_mk_number(mjs, 1), TOK_MINUS); |
||
9517 |
mjs_set_v(mjs, obj, key, v1); |
||
9518 |
mjs_push(mjs, v); |
||
9519 |
} else { |
||
9520 |
mjs_set_errorf(mjs, MJS_TYPE_ERROR, "invalid operand for --"); |
||
9521 |
} |
||
9522 |
break; |
||
9523 |
} |
||
9524 |
case TOK_MINUS_MINUS: { |
||
9525 |
2 |
mjs_val_t obj = mjs_pop(mjs); |
|
9526 |
2 |
mjs_val_t key = mjs_pop(mjs); |
|
9527 |
✗✓✗✗ |
2 |
if (mjs_is_object(obj) && mjs_is_string(key)) { |
9528 |
mjs_val_t v = mjs_get_v(mjs, obj, key); |
||
9529 |
v = do_op(mjs, v, mjs_mk_number(mjs, 1), TOK_MINUS); |
||
9530 |
mjs_set_v(mjs, obj, key, v); |
||
9531 |
mjs_push(mjs, v); |
||
9532 |
} else { |
||
9533 |
2 |
mjs_set_errorf(mjs, MJS_TYPE_ERROR, "invalid operand for --"); |
|
9534 |
} |
||
9535 |
2 |
break; |
|
9536 |
} |
||
9537 |
case TOK_PLUS_PLUS: { |
||
9538 |
mjs_val_t obj = mjs_pop(mjs); |
||
9539 |
mjs_val_t key = mjs_pop(mjs); |
||
9540 |
if (mjs_is_object(obj) && mjs_is_string(key)) { |
||
9541 |
mjs_val_t v = mjs_get_v(mjs, obj, key); |
||
9542 |
v = do_op(mjs, v, mjs_mk_number(mjs, 1), TOK_PLUS); |
||
9543 |
mjs_set_v(mjs, obj, key, v); |
||
9544 |
mjs_push(mjs, v); |
||
9545 |
} else { |
||
9546 |
mjs_set_errorf(mjs, MJS_TYPE_ERROR, "invalid operand for ++"); |
||
9547 |
} |
||
9548 |
break; |
||
9549 |
} |
||
9550 |
/* |
||
9551 |
* NOTE: TOK_LOGICAL_AND and TOK_LOGICAL_OR don't need to be here, because |
||
9552 |
* they are just naturally handled by the short-circuit evaluation. |
||
9553 |
* See PARSE_LTR_BINOP() macro in mjs_parser.c. |
||
9554 |
*/ |
||
9555 |
|||
9556 |
/* clang-format off */ |
||
9557 |
case TOK_MINUS_ASSIGN: op_assign(mjs, TOK_MINUS); break; |
||
9558 |
case TOK_PLUS_ASSIGN: op_assign(mjs, TOK_PLUS); break; |
||
9559 |
case TOK_MUL_ASSIGN: op_assign(mjs, TOK_MUL); break; |
||
9560 |
case TOK_DIV_ASSIGN: op_assign(mjs, TOK_DIV); break; |
||
9561 |
case TOK_REM_ASSIGN: op_assign(mjs, TOK_REM); break; |
||
9562 |
case TOK_AND_ASSIGN: op_assign(mjs, TOK_AND); break; |
||
9563 |
case TOK_OR_ASSIGN: op_assign(mjs, TOK_OR); break; |
||
9564 |
case TOK_XOR_ASSIGN: op_assign(mjs, TOK_XOR); break; |
||
9565 |
case TOK_LSHIFT_ASSIGN: op_assign(mjs, TOK_LSHIFT); break; |
||
9566 |
case TOK_RSHIFT_ASSIGN: op_assign(mjs, TOK_RSHIFT); break; |
||
9567 |
case TOK_URSHIFT_ASSIGN: op_assign(mjs, TOK_URSHIFT); break; |
||
9568 |
case TOK_COMMA: break; |
||
9569 |
/* clang-format on */ |
||
9570 |
case TOK_KEYWORD_TYPEOF: |
||
9571 |
1 |
mjs_push(mjs, mjs_mk_string(mjs, mjs_typeof(mjs_pop(mjs)), ~0, 1)); |
|
9572 |
1 |
break; |
|
9573 |
default: |
||
9574 |
LOG(LL_ERROR, ("Unknown expr: %d", op)); |
||
9575 |
break; |
||
9576 |
} |
||
9577 |
13 |
} |
|
9578 |
|||
9579 |
static int getprop_builtin_string(struct mjs *mjs, mjs_val_t val, |
||
9580 |
const char *name, size_t name_len, |
||
9581 |
mjs_val_t *res) { |
||
9582 |
int isnum = 0; |
||
9583 |
int idx = cstr_to_ulong(name, name_len, &isnum); |
||
9584 |
|||
9585 |
if (strcmp(name, "length") == 0) { |
||
9586 |
size_t val_len; |
||
9587 |
mjs_get_string(mjs, &val, &val_len); |
||
9588 |
*res = mjs_mk_number(mjs, (double) val_len); |
||
9589 |
return 1; |
||
9590 |
} else if (strcmp(name, "at") == 0 || strcmp(name, "charCodeAt") == 0) { |
||
9591 |
*res = mjs_mk_foreign_func(mjs, (mjs_func_ptr_t) mjs_string_char_code_at); |
||
9592 |
return 1; |
||
9593 |
} else if (strcmp(name, "indexOf") == 0) { |
||
9594 |
*res = mjs_mk_foreign_func(mjs, (mjs_func_ptr_t) mjs_string_index_of); |
||
9595 |
return 1; |
||
9596 |
} else if (strcmp(name, "slice") == 0) { |
||
9597 |
*res = mjs_mk_foreign_func(mjs, (mjs_func_ptr_t) mjs_string_slice); |
||
9598 |
return 1; |
||
9599 |
} else if (isnum) { |
||
9600 |
/* |
||
9601 |
* string subscript: return a new one-byte string if the index |
||
9602 |
* is not out of bounds |
||
9603 |
*/ |
||
9604 |
size_t val_len; |
||
9605 |
const char *str = mjs_get_string(mjs, &val, &val_len); |
||
9606 |
if (idx >= 0 && idx < (int) val_len) { |
||
9607 |
*res = mjs_mk_string(mjs, str + idx, 1, 1); |
||
9608 |
} else { |
||
9609 |
*res = MJS_UNDEFINED; |
||
9610 |
} |
||
9611 |
return 1; |
||
9612 |
} |
||
9613 |
return 0; |
||
9614 |
} |
||
9615 |
|||
9616 |
static int getprop_builtin_array(struct mjs *mjs, mjs_val_t val, |
||
9617 |
const char *name, size_t name_len, |
||
9618 |
mjs_val_t *res) { |
||
9619 |
if (strcmp(name, "splice") == 0) { |
||
9620 |
*res = mjs_mk_foreign_func(mjs, (mjs_func_ptr_t) mjs_array_splice); |
||
9621 |
return 1; |
||
9622 |
} else if (strcmp(name, "push") == 0) { |
||
9623 |
*res = mjs_mk_foreign_func(mjs, (mjs_func_ptr_t) mjs_array_push_internal); |
||
9624 |
return 1; |
||
9625 |
} else if (strcmp(name, "length") == 0) { |
||
9626 |
*res = mjs_mk_number(mjs, mjs_array_length(mjs, val)); |
||
9627 |
return 1; |
||
9628 |
} |
||
9629 |
|||
9630 |
(void) name_len; |
||
9631 |
return 0; |
||
9632 |
} |
||
9633 |
|||
9634 |
static int getprop_builtin_foreign(struct mjs *mjs, mjs_val_t val, |
||
9635 |
const char *name, size_t name_len, |
||
9636 |
mjs_val_t *res) { |
||
9637 |
int isnum = 0; |
||
9638 |
int idx = cstr_to_ulong(name, name_len, &isnum); |
||
9639 |
|||
9640 |
if (!isnum) { |
||
9641 |
mjs_prepend_errorf(mjs, MJS_TYPE_ERROR, "index must be a number"); |
||
9642 |
} else { |
||
9643 |
uint8_t *ptr = (uint8_t *) mjs_get_ptr(mjs, val); |
||
9644 |
*res = mjs_mk_number(mjs, *(ptr + idx)); |
||
9645 |
} |
||
9646 |
return 1; |
||
9647 |
} |
||
9648 |
|||
9649 |
static void mjs_apply_(struct mjs *mjs) { |
||
9650 |
mjs_val_t res = MJS_UNDEFINED, *args = NULL; |
||
9651 |
mjs_val_t func = mjs->vals.this_obj, v = mjs_arg(mjs, 1); |
||
9652 |
int i, nargs = 0; |
||
9653 |
if (mjs_is_array(v)) { |
||
9654 |
nargs = mjs_array_length(mjs, v); |
||
9655 |
args = calloc(nargs, sizeof(args[0])); |
||
9656 |
for (i = 0; i < nargs; i++) args[i] = mjs_array_get(mjs, v, i); |
||
9657 |
} |
||
9658 |
mjs_apply(mjs, &res, func, mjs_arg(mjs, 0), nargs, args); |
||
9659 |
free(args); |
||
9660 |
mjs_return(mjs, res); |
||
9661 |
} |
||
9662 |
|||
9663 |
static int getprop_builtin(struct mjs *mjs, mjs_val_t val, mjs_val_t name, |
||
9664 |
mjs_val_t *res) { |
||
9665 |
size_t n; |
||
9666 |
char *s = NULL; |
||
9667 |
int need_free = 0; |
||
9668 |
int handled = 0; |
||
9669 |
|||
9670 |
mjs_err_t err = mjs_to_string(mjs, &name, &s, &n, &need_free); |
||
9671 |
|||
9672 |
if (err == MJS_OK) { |
||
9673 |
if (mjs_is_string(val)) { |
||
9674 |
handled = getprop_builtin_string(mjs, val, s, n, res); |
||
9675 |
} else if (s != NULL && n == 5 && strncmp(s, "apply", n) == 0) { |
||
9676 |
*res = mjs_mk_foreign_func(mjs, (mjs_func_ptr_t) mjs_apply_); |
||
9677 |
handled = 1; |
||
9678 |
} else if (mjs_is_array(val)) { |
||
9679 |
handled = getprop_builtin_array(mjs, val, s, n, res); |
||
9680 |
} else if (mjs_is_foreign(val)) { |
||
9681 |
handled = getprop_builtin_foreign(mjs, val, s, n, res); |
||
9682 |
} |
||
9683 |
} |
||
9684 |
|||
9685 |
if (need_free) { |
||
9686 |
free(s); |
||
9687 |
s = NULL; |
||
9688 |
} |
||
9689 |
|||
9690 |
return handled; |
||
9691 |
} |
||
9692 |
|||
9693 |
66 |
MJS_PRIVATE mjs_err_t mjs_execute(struct mjs *mjs, size_t off, mjs_val_t *res) { |
|
9694 |
size_t i; |
||
9695 |
66 |
uint8_t prev_opcode = OP_MAX; |
|
9696 |
66 |
uint8_t opcode = OP_MAX; |
|
9697 |
|||
9698 |
/* |
||
9699 |
* remember lengths of all stacks, they will be restored in case of an error |
||
9700 |
*/ |
||
9701 |
66 |
int stack_len = mjs->stack.len; |
|
9702 |
66 |
int call_stack_len = mjs->call_stack.len; |
|
9703 |
66 |
int arg_stack_len = mjs->arg_stack.len; |
|
9704 |
66 |
int scopes_len = mjs->scopes.len; |
|
9705 |
66 |
int loop_addresses_len = mjs->loop_addresses.len; |
|
9706 |
66 |
size_t start_off = off; |
|
9707 |
const uint8_t *code; |
||
9708 |
|||
9709 |
66 |
struct mjs_bcode_part bp = *mjs_bcode_part_get_by_offset(mjs, off); |
|
9710 |
|||
9711 |
66 |
mjs_set_errorf(mjs, MJS_OK, NULL); |
|
9712 |
66 |
free(mjs->stack_trace); |
|
9713 |
66 |
mjs->stack_trace = NULL; |
|
9714 |
|||
9715 |
66 |
off -= bp.start_idx; |
|
9716 |
|||
9717 |
✓✓ | 264 |
for (i = off; i < bp.data.len; i++) { |
9718 |
248 |
mjs->cur_bcode_offset = i; |
|
9719 |
|||
9720 |
✓✓ | 248 |
if (mjs->need_gc) { |
9721 |
✓✗ | 67 |
if (maybe_gc(mjs)) { |
9722 |
67 |
mjs->need_gc = 0; |
|
9723 |
} |
||
9724 |
} |
||
9725 |
#if MJS_AGGRESSIVE_GC |
||
9726 |
maybe_gc(mjs); |
||
9727 |
#endif |
||
9728 |
|||
9729 |
248 |
code = (const uint8_t *) bp.data.p; |
|
9730 |
248 |
mjs_disasm_single(code, i); |
|
9731 |
248 |
prev_opcode = opcode; |
|
9732 |
248 |
opcode = code[i]; |
|
9733 |
✓✓✓✓ ✓✓✗✗ ✓✗✗✗ ✗✓✓✗ ✗✗✗✓ ✓✓✗✗ ✗✗✗✗ ✓✓✓✗ ✗✗✓✓ ✗✓✗ |
248 |
switch (opcode) { |
9734 |
case OP_BCODE_HEADER: { |
||
9735 |
mjs_header_item_t bcode_offset; |
||
9736 |
66 |
memcpy(&bcode_offset, |
|
9737 |
66 |
code + i + 1 + |
|
9738 |
sizeof(mjs_header_item_t) * MJS_HDR_ITEM_BCODE_OFFSET, |
||
9739 |
sizeof(bcode_offset)); |
||
9740 |
66 |
i += bcode_offset; |
|
9741 |
66 |
} break; |
|
9742 |
case OP_PUSH_NULL: |
||
9743 |
2 |
mjs_push(mjs, mjs_mk_null()); |
|
9744 |
2 |
break; |
|
9745 |
case OP_PUSH_UNDEF: |
||
9746 |
5 |
mjs_push(mjs, mjs_mk_undefined()); |
|
9747 |
5 |
break; |
|
9748 |
case OP_PUSH_FALSE: |
||
9749 |
2 |
mjs_push(mjs, mjs_mk_boolean(mjs, 0)); |
|
9750 |
2 |
break; |
|
9751 |
case OP_PUSH_TRUE: |
||
9752 |
1 |
mjs_push(mjs, mjs_mk_boolean(mjs, 1)); |
|
9753 |
1 |
break; |
|
9754 |
case OP_PUSH_OBJ: |
||
9755 |
1 |
mjs_push(mjs, mjs_mk_object(mjs)); |
|
9756 |
1 |
break; |
|
9757 |
case OP_PUSH_ARRAY: |
||
9758 |
mjs_push(mjs, mjs_mk_array(mjs)); |
||
9759 |
break; |
||
9760 |
case OP_PUSH_FUNC: { |
||
9761 |
int llen, n = cs_varint_decode_unsafe(&code[i + 1], &llen); |
||
9762 |
mjs_push(mjs, mjs_mk_function(mjs, bp.start_idx + i - n)); |
||
9763 |
i += llen; |
||
9764 |
break; |
||
9765 |
} |
||
9766 |
case OP_PUSH_THIS: |
||
9767 |
1 |
mjs_push(mjs, mjs->vals.this_obj); |
|
9768 |
1 |
break; |
|
9769 |
case OP_JMP: { |
||
9770 |
int llen, n = cs_varint_decode_unsafe(&code[i + 1], &llen); |
||
9771 |
i += n + llen; |
||
9772 |
break; |
||
9773 |
} |
||
9774 |
case OP_JMP_FALSE: { |
||
9775 |
int llen, n = cs_varint_decode_unsafe(&code[i + 1], &llen); |
||
9776 |
i += llen; |
||
9777 |
if (!mjs_is_truthy(mjs, mjs_pop(mjs))) { |
||
9778 |
mjs_push(mjs, MJS_UNDEFINED); |
||
9779 |
i += n; |
||
9780 |
} |
||
9781 |
break; |
||
9782 |
} |
||
9783 |
/* |
||
9784 |
* OP_JMP_NEUTRAL_... ops are like as OP_JMP_..., but they are completely |
||
9785 |
* stack-neutral: they just check the TOS, and increment instruction |
||
9786 |
* pointer if the TOS is truthy/falsy. |
||
9787 |
*/ |
||
9788 |
case OP_JMP_NEUTRAL_TRUE: { |
||
9789 |
int llen, n = cs_varint_decode_unsafe(&code[i + 1], &llen); |
||
9790 |
i += llen; |
||
9791 |
if (mjs_is_truthy(mjs, vtop(&mjs->stack))) { |
||
9792 |
i += n; |
||
9793 |
} |
||
9794 |
break; |
||
9795 |
} |
||
9796 |
case OP_JMP_NEUTRAL_FALSE: { |
||
9797 |
int llen, n = cs_varint_decode_unsafe(&code[i + 1], &llen); |
||
9798 |
i += llen; |
||
9799 |
if (!mjs_is_truthy(mjs, vtop(&mjs->stack))) { |
||
9800 |
i += n; |
||
9801 |
} |
||
9802 |
break; |
||
9803 |
} |
||
9804 |
case OP_FIND_SCOPE: { |
||
9805 |
40 |
mjs_val_t key = vtop(&mjs->stack); |
|
9806 |
40 |
mjs_push(mjs, mjs_find_scope(mjs, key)); |
|
9807 |
40 |
break; |
|
9808 |
} |
||
9809 |
case OP_CREATE: { |
||
9810 |
1 |
mjs_val_t obj = mjs_pop(mjs); |
|
9811 |
1 |
mjs_val_t key = mjs_pop(mjs); |
|
9812 |
✓✗ | 1 |
if (mjs_get_own_property_v(mjs, obj, key) == NULL) { |
9813 |
1 |
mjs_set_v(mjs, obj, key, MJS_UNDEFINED); |
|
9814 |
} |
||
9815 |
1 |
break; |
|
9816 |
} |
||
9817 |
case OP_APPEND: { |
||
9818 |
mjs_val_t val = mjs_pop(mjs); |
||
9819 |
mjs_val_t arr = mjs_pop(mjs); |
||
9820 |
mjs_err_t err = mjs_array_push(mjs, arr, val); |
||
9821 |
if (err != MJS_OK) { |
||
9822 |
mjs_set_errorf(mjs, MJS_TYPE_ERROR, "append to non-array"); |
||
9823 |
} |
||
9824 |
break; |
||
9825 |
} |
||
9826 |
case OP_GET: { |
||
9827 |
mjs_val_t obj = mjs_pop(mjs); |
||
9828 |
mjs_val_t key = mjs_pop(mjs); |
||
9829 |
mjs_val_t val = MJS_UNDEFINED; |
||
9830 |
|||
9831 |
if (!getprop_builtin(mjs, obj, key, &val)) { |
||
9832 |
if (mjs_is_object(obj)) { |
||
9833 |
val = mjs_get_v_proto(mjs, obj, key); |
||
9834 |
} else { |
||
9835 |
mjs_prepend_errorf(mjs, MJS_TYPE_ERROR, "type error"); |
||
9836 |
} |
||
9837 |
} |
||
9838 |
|||
9839 |
mjs_push(mjs, val); |
||
9840 |
if (prev_opcode != OP_FIND_SCOPE) { |
||
9841 |
/* |
||
9842 |
* Previous opcode was not OP_FIND_SCOPE, so it's some "custom" |
||
9843 |
* object which might be used as `this`, so, save it |
||
9844 |
*/ |
||
9845 |
mjs->vals.last_getprop_obj = obj; |
||
9846 |
} else { |
||
9847 |
/* |
||
9848 |
* Previous opcode was OP_FIND_SCOPE, so we're getting value from |
||
9849 |
* the scope, and it should *not* be used as `this` |
||
9850 |
*/ |
||
9851 |
mjs->vals.last_getprop_obj = MJS_UNDEFINED; |
||
9852 |
} |
||
9853 |
break; |
||
9854 |
} |
||
9855 |
case OP_DEL_SCOPE: |
||
9856 |
if (mjs->scopes.len <= 1) { |
||
9857 |
mjs_set_errorf(mjs, MJS_INTERNAL_ERROR, "scopes underflow"); |
||
9858 |
} else { |
||
9859 |
mjs_pop_val(&mjs->scopes); |
||
9860 |
} |
||
9861 |
break; |
||
9862 |
case OP_NEW_SCOPE: |
||
9863 |
push_mjs_val(&mjs->scopes, mjs_mk_object(mjs)); |
||
9864 |
break; |
||
9865 |
case OP_PUSH_SCOPE: |
||
9866 |
✗✓ | 1 |
assert(mjs_stack_size(&mjs->scopes) > 0); |
9867 |
1 |
mjs_push(mjs, vtop(&mjs->scopes)); |
|
9868 |
1 |
break; |
|
9869 |
case OP_PUSH_STR: { |
||
9870 |
50 |
int llen, n = cs_varint_decode_unsafe(&code[i + 1], &llen); |
|
9871 |
50 |
mjs_push(mjs, mjs_mk_string(mjs, (char *) code + i + 1 + llen, n, 1)); |
|
9872 |
50 |
i += llen + n; |
|
9873 |
50 |
break; |
|
9874 |
} |
||
9875 |
case OP_PUSH_INT: { |
||
9876 |
int llen; |
||
9877 |
31 |
int64_t n = cs_varint_decode_unsafe(&code[i + 1], &llen); |
|
9878 |
31 |
mjs_push(mjs, mjs_mk_number(mjs, (double) n)); |
|
9879 |
31 |
i += llen; |
|
9880 |
31 |
break; |
|
9881 |
} |
||
9882 |
case OP_PUSH_DBL: { |
||
9883 |
int llen, n = cs_varint_decode_unsafe(&code[i + 1], &llen); |
||
9884 |
mjs_push(mjs, mjs_mk_number( |
||
9885 |
mjs, strtod((char *) code + i + 1 + llen, NULL))); |
||
9886 |
i += llen + n; |
||
9887 |
break; |
||
9888 |
} |
||
9889 |
case OP_FOR_IN_NEXT: { |
||
9890 |
/* |
||
9891 |
* Data stack layout: |
||
9892 |
* ... <-- Bottom of the data stack |
||
9893 |
* <iterator_variable_name> (string) |
||
9894 |
* <object_that_is_iterated> (object) |
||
9895 |
* <iterator_foreign_ptr> <-- Top of the data stack |
||
9896 |
*/ |
||
9897 |
mjs_val_t *iterator = vptr(&mjs->stack, -1); |
||
9898 |
mjs_val_t obj = *vptr(&mjs->stack, -2); |
||
9899 |
if (mjs_is_object(obj)) { |
||
9900 |
mjs_val_t var_name = *vptr(&mjs->stack, -3); |
||
9901 |
mjs_val_t key = mjs_next(mjs, obj, iterator); |
||
9902 |
if (key != MJS_UNDEFINED) { |
||
9903 |
mjs_val_t scope = mjs_find_scope(mjs, var_name); |
||
9904 |
mjs_set_v(mjs, scope, var_name, key); |
||
9905 |
} |
||
9906 |
} else { |
||
9907 |
mjs_set_errorf(mjs, MJS_TYPE_ERROR, |
||
9908 |
"can't iterate over non-object value"); |
||
9909 |
} |
||
9910 |
break; |
||
9911 |
} |
||
9912 |
case OP_RETURN: { |
||
9913 |
/* |
||
9914 |
* Return address is saved as a global bcode offset, so we need to |
||
9915 |
* convert it to the local offset |
||
9916 |
*/ |
||
9917 |
size_t off_ret = call_stack_restore_frame(mjs); |
||
9918 |
if (off_ret != MJS_BCODE_OFFSET_EXIT) { |
||
9919 |
bp = *mjs_bcode_part_get_by_offset(mjs, off_ret); |
||
9920 |
code = (const uint8_t *) bp.data.p; |
||
9921 |
i = off_ret - bp.start_idx; |
||
9922 |
LOG(LL_VERBOSE_DEBUG, ("RETURNING TO %d", (int) off_ret + 1)); |
||
9923 |
} else { |
||
9924 |
goto clean; |
||
9925 |
} |
||
9926 |
// mjs_dump(mjs, 0, stdout); |
||
9927 |
break; |
||
9928 |
} |
||
9929 |
case OP_ARGS: { |
||
9930 |
/* |
||
9931 |
* If OP_ARGS follows OP_GET, then last_getprop_obj is set to `this` |
||
9932 |
* value; otherwise, last_getprop_obj is irrelevant and we have to |
||
9933 |
* reset it to `undefined` |
||
9934 |
*/ |
||
9935 |
if (prev_opcode != OP_GET) { |
||
9936 |
mjs->vals.last_getprop_obj = MJS_UNDEFINED; |
||
9937 |
} |
||
9938 |
|||
9939 |
/* |
||
9940 |
* Push last_getprop_obj, which is going to be used as `this`, see |
||
9941 |
* OP_CALL |
||
9942 |
*/ |
||
9943 |
push_mjs_val(&mjs->arg_stack, mjs->vals.last_getprop_obj); |
||
9944 |
/* |
||
9945 |
* Push current size of data stack, it's needed to place arguments |
||
9946 |
* properly |
||
9947 |
*/ |
||
9948 |
push_mjs_val(&mjs->arg_stack, |
||
9949 |
mjs_mk_number(mjs, (double) mjs_stack_size(&mjs->stack))); |
||
9950 |
break; |
||
9951 |
} |
||
9952 |
case OP_CALL: { |
||
9953 |
// LOG(LL_INFO, ("BEFORE CALL")); |
||
9954 |
// mjs_dump(mjs, 0, stdout); |
||
9955 |
int func_pos; |
||
9956 |
mjs_val_t *func; |
||
9957 |
mjs_val_t retval_stack_idx = vtop(&mjs->arg_stack); |
||
9958 |
func_pos = mjs_get_int(mjs, retval_stack_idx) - 1; |
||
9959 |
func = vptr(&mjs->stack, func_pos); |
||
9960 |
|||
9961 |
/* Drop data stack size (pushed by OP_ARGS) */ |
||
9962 |
mjs_pop_val(&mjs->arg_stack); |
||
9963 |
|||
9964 |
if (mjs_is_function(*func)) { |
||
9965 |
size_t off_call; |
||
9966 |
call_stack_push_frame(mjs, bp.start_idx + i, retval_stack_idx); |
||
9967 |
|||
9968 |
/* |
||
9969 |
* Function offset is a global bcode offset, so we need to convert it |
||
9970 |
* to the local offset |
||
9971 |
*/ |
||
9972 |
off_call = mjs_get_func_addr(*func) - 1; |
||
9973 |
bp = *mjs_bcode_part_get_by_offset(mjs, off_call); |
||
9974 |
code = (const uint8_t *) bp.data.p; |
||
9975 |
i = off_call - bp.start_idx; |
||
9976 |
|||
9977 |
*func = MJS_UNDEFINED; // Return value |
||
9978 |
// LOG(LL_VERBOSE_DEBUG, ("CALLING %d", i + 1)); |
||
9979 |
} else if (mjs_is_string(*func) || mjs_is_ffi_sig(*func)) { |
||
9980 |
/* Call ffi-ed function */ |
||
9981 |
|||
9982 |
call_stack_push_frame(mjs, bp.start_idx + i, retval_stack_idx); |
||
9983 |
|||
9984 |
/* Perform the ffi-ed function call */ |
||
9985 |
mjs_ffi_call2(mjs); |
||
9986 |
|||
9987 |
call_stack_restore_frame(mjs); |
||
9988 |
} else if (mjs_is_foreign(*func)) { |
||
9989 |
/* Call cfunction */ |
||
9990 |
|||
9991 |
call_stack_push_frame(mjs, bp.start_idx + i, retval_stack_idx); |
||
9992 |
|||
9993 |
/* Perform the cfunction call */ |
||
9994 |
((void (*) (struct mjs *)) mjs_get_ptr(mjs, *func))(mjs); |
||
9995 |
|||
9996 |
call_stack_restore_frame(mjs); |
||
9997 |
} else { |
||
9998 |
mjs_set_errorf(mjs, MJS_TYPE_ERROR, "calling non-callable"); |
||
9999 |
} |
||
10000 |
break; |
||
10001 |
} |
||
10002 |
case OP_SET_ARG: { |
||
10003 |
int llen1, llen2, n, |
||
10004 |
arg_no = cs_varint_decode_unsafe(&code[i + 1], &llen1); |
||
10005 |
mjs_val_t obj, key, v; |
||
10006 |
n = cs_varint_decode_unsafe(&code[i + llen1 + 1], &llen2); |
||
10007 |
key = mjs_mk_string(mjs, (char *) code + i + 1 + llen1 + llen2, n, 1); |
||
10008 |
obj = vtop(&mjs->scopes); |
||
10009 |
v = mjs_arg(mjs, arg_no); |
||
10010 |
mjs_set_v(mjs, obj, key, v); |
||
10011 |
i += llen1 + llen2 + n; |
||
10012 |
break; |
||
10013 |
} |
||
10014 |
case OP_SETRETVAL: { |
||
10015 |
✓✗ | 2 |
if (mjs_stack_size(&mjs->call_stack) < CALL_STACK_FRAME_ITEMS_CNT) { |
10016 |
2 |
mjs_set_errorf(mjs, MJS_INTERNAL_ERROR, "cannot return"); |
|
10017 |
} else { |
||
10018 |
size_t retval_pos = mjs_get_int( |
||
10019 |
mjs, *vptr(&mjs->call_stack, |
||
10020 |
-1 - CALL_STACK_FRAME_ITEM_RETVAL_STACK_IDX)); |
||
10021 |
*vptr(&mjs->stack, retval_pos - 1) = mjs_pop(mjs); |
||
10022 |
} |
||
10023 |
// LOG(LL_INFO, ("AFTER SETRETVAL")); |
||
10024 |
// mjs_dump(mjs, 0, stdout); |
||
10025 |
2 |
break; |
|
10026 |
} |
||
10027 |
case OP_EXPR: { |
||
10028 |
13 |
int op = code[i + 1]; |
|
10029 |
13 |
exec_expr(mjs, op); |
|
10030 |
13 |
i++; |
|
10031 |
13 |
break; |
|
10032 |
} |
||
10033 |
case OP_DROP: { |
||
10034 |
14 |
mjs_pop(mjs); |
|
10035 |
14 |
break; |
|
10036 |
} |
||
10037 |
case OP_DUP: { |
||
10038 |
mjs_push(mjs, vtop(&mjs->stack)); |
||
10039 |
break; |
||
10040 |
} |
||
10041 |
case OP_SWAP: { |
||
10042 |
mjs_val_t a = mjs_pop(mjs); |
||
10043 |
mjs_val_t b = mjs_pop(mjs); |
||
10044 |
mjs_push(mjs, a); |
||
10045 |
mjs_push(mjs, b); |
||
10046 |
break; |
||
10047 |
} |
||
10048 |
case OP_LOOP: { |
||
10049 |
int l1, l2, off = cs_varint_decode_unsafe(&code[i + 1], &l1); |
||
10050 |
/* push scope index */ |
||
10051 |
push_mjs_val(&mjs->loop_addresses, |
||
10052 |
mjs_mk_number(mjs, (double) mjs_stack_size(&mjs->scopes))); |
||
10053 |
|||
10054 |
/* push break offset */ |
||
10055 |
push_mjs_val( |
||
10056 |
&mjs->loop_addresses, |
||
10057 |
mjs_mk_number(mjs, (double) (i + 1 /* OP_LOOP */ + l1 + off))); |
||
10058 |
off = cs_varint_decode_unsafe(&code[i + 1 + l1], &l2); |
||
10059 |
|||
10060 |
/* push continue offset */ |
||
10061 |
push_mjs_val( |
||
10062 |
&mjs->loop_addresses, |
||
10063 |
mjs_mk_number(mjs, (double) (i + 1 /* OP_LOOP*/ + l1 + l2 + off))); |
||
10064 |
i += l1 + l2; |
||
10065 |
break; |
||
10066 |
} |
||
10067 |
case OP_CONTINUE: { |
||
10068 |
✗✓ | 1 |
if (mjs_stack_size(&mjs->loop_addresses) >= 3) { |
10069 |
size_t scopes_len = mjs_get_int(mjs, *vptr(&mjs->loop_addresses, -3)); |
||
10070 |
assert(mjs_stack_size(&mjs->scopes) >= scopes_len); |
||
10071 |
mjs->scopes.len = scopes_len * sizeof(mjs_val_t); |
||
10072 |
|||
10073 |
/* jump to "continue" address */ |
||
10074 |
i = mjs_get_int(mjs, vtop(&mjs->loop_addresses)) - 1; |
||
10075 |
} else { |
||
10076 |
1 |
mjs_set_errorf(mjs, MJS_SYNTAX_ERROR, "misplaced 'continue'"); |
|
10077 |
} |
||
10078 |
1 |
} break; |
|
10079 |
case OP_BREAK: { |
||
10080 |
✗✓ | 1 |
if (mjs_stack_size(&mjs->loop_addresses) >= 3) { |
10081 |
size_t scopes_len; |
||
10082 |
/* drop "continue" address */ |
||
10083 |
mjs_pop_val(&mjs->loop_addresses); |
||
10084 |
|||
10085 |
/* pop "break" address and jump to it */ |
||
10086 |
i = mjs_get_int(mjs, mjs_pop_val(&mjs->loop_addresses)) - 1; |
||
10087 |
|||
10088 |
/* restore scope index */ |
||
10089 |
scopes_len = mjs_get_int(mjs, mjs_pop_val(&mjs->loop_addresses)); |
||
10090 |
assert(mjs_stack_size(&mjs->scopes) >= scopes_len); |
||
10091 |
mjs->scopes.len = scopes_len * sizeof(mjs_val_t); |
||
10092 |
|||
10093 |
LOG(LL_VERBOSE_DEBUG, ("BREAKING TO %d", (int) i + 1)); |
||
10094 |
} else { |
||
10095 |
1 |
mjs_set_errorf(mjs, MJS_SYNTAX_ERROR, "misplaced 'break'"); |
|
10096 |
} |
||
10097 |
1 |
} break; |
|
10098 |
case OP_NOP: |
||
10099 |
break; |
||
10100 |
case OP_EXIT: |
||
10101 |
16 |
i = bp.data.len; |
|
10102 |
16 |
break; |
|
10103 |
default: |
||
10104 |
#if MJS_ENABLE_DEBUG |
||
10105 |
mjs_dump(mjs, 1); |
||
10106 |
#endif |
||
10107 |
mjs_set_errorf(mjs, MJS_INTERNAL_ERROR, "Unknown opcode: %d, off %d+%d", |
||
10108 |
(int) opcode, (int) bp.start_idx, (int) i); |
||
10109 |
i = bp.data.len; |
||
10110 |
break; |
||
10111 |
} |
||
10112 |
✓✓ | 248 |
if (mjs->error != MJS_OK) { |
10113 |
50 |
mjs_gen_stack_trace(mjs, bp.start_idx + i - 1 /* undo the i++ */); |
|
10114 |
|||
10115 |
/* restore stack lenghts */ |
||
10116 |
50 |
mjs->stack.len = stack_len; |
|
10117 |
50 |
mjs->call_stack.len = call_stack_len; |
|
10118 |
50 |
mjs->arg_stack.len = arg_stack_len; |
|
10119 |
50 |
mjs->scopes.len = scopes_len; |
|
10120 |
50 |
mjs->loop_addresses.len = loop_addresses_len; |
|
10121 |
|||
10122 |
/* script will evaluate to `undefined` */ |
||
10123 |
50 |
mjs_push(mjs, MJS_UNDEFINED); |
|
10124 |
50 |
break; |
|
10125 |
} |
||
10126 |
} |
||
10127 |
|||
10128 |
clean: |
||
10129 |
/* Remember result of the evaluation of this bcode part */ |
||
10130 |
66 |
mjs_bcode_part_get_by_offset(mjs, start_off)->exec_res = mjs->error; |
|
10131 |
|||
10132 |
66 |
*res = mjs_pop(mjs); |
|
10133 |
66 |
return mjs->error; |
|
10134 |
} |
||
10135 |
|||
10136 |
78 |
MJS_PRIVATE mjs_err_t mjs_exec_internal(struct mjs *mjs, const char *path, |
|
10137 |
const char *src, int generate_jsc, |
||
10138 |
mjs_val_t *res) { |
||
10139 |
78 |
size_t off = mjs->bcode_len; |
|
10140 |
78 |
mjs_val_t r = MJS_UNDEFINED; |
|
10141 |
78 |
mjs->error = mjs_parse(path, src, mjs); |
|
10142 |
✗✓ | 78 |
if (cs_log_threshold >= LL_VERBOSE_DEBUG) mjs_dump(mjs, 1); |
10143 |
✗✓ | 78 |
if (generate_jsc == -1) generate_jsc = mjs->generate_jsc; |
10144 |
✓✓ | 78 |
if (mjs->error == MJS_OK) { |
10145 |
#if MJS_GENERATE_JSC && defined(CS_MMAP) |
||
10146 |
if (generate_jsc && path != NULL) { |
||
10147 |
const char *jsext = ".js"; |
||
10148 |
int basename_len = (int) strlen(path) - strlen(jsext); |
||
10149 |
if (basename_len > 0 && strcmp(path + basename_len, jsext) == 0) { |
||
10150 |
/* source file has a .js extension: create a .jsc counterpart */ |
||
10151 |
int rewrite = 1; |
||
10152 |
int read_mmapped = 1; |
||
10153 |
|||
10154 |
/* construct .jsc filename */ |
||
10155 |
const char *jscext = ".jsc"; |
||
10156 |
char filename_jsc[basename_len + strlen(jscext) + 1 /* nul-term */]; |
||
10157 |
memcpy(filename_jsc, path, basename_len); |
||
10158 |
strcpy(filename_jsc + basename_len, jscext); |
||
10159 |
|||
10160 |
/* get last bcode part */ |
||
10161 |
struct mjs_bcode_part *bp = |
||
10162 |
mjs_bcode_part_get(mjs, mjs_bcode_parts_cnt(mjs) - 1); |
||
10163 |
|||
10164 |
/* |
||
10165 |
* before writing .jsc file, check if it already exists and has the |
||
10166 |
* same contents |
||
10167 |
* |
||
10168 |
* TODO(dfrank): probably store crc32 before the bcode data, and only |
||
10169 |
* compare it. |
||
10170 |
*/ |
||
10171 |
{ |
||
10172 |
size_t size; |
||
10173 |
char *data = cs_mmap_file(filename_jsc, &size); |
||
10174 |
if (data != NULL) { |
||
10175 |
if (size == bp->data.len) { |
||
10176 |
if (memcmp(data, bp->data.p, size) == 0) { |
||
10177 |
/* .jsc file is up to date, so don't rewrite it */ |
||
10178 |
rewrite = 0; |
||
10179 |
} |
||
10180 |
} |
||
10181 |
munmap(data, size); |
||
10182 |
} |
||
10183 |
} |
||
10184 |
|||
10185 |
/* try to open .jsc file for writing */ |
||
10186 |
if (rewrite) { |
||
10187 |
FILE *fp = fopen(filename_jsc, "wb"); |
||
10188 |
if (fp != NULL) { |
||
10189 |
/* write last bcode part to .jsc */ |
||
10190 |
fwrite(bp->data.p, bp->data.len, 1, fp); |
||
10191 |
fclose(fp); |
||
10192 |
} else { |
||
10193 |
LOG(LL_WARN, ("Failed to open %s for writing", filename_jsc)); |
||
10194 |
read_mmapped = 0; |
||
10195 |
} |
||
10196 |
} |
||
10197 |
|||
10198 |
if (read_mmapped) { |
||
10199 |
/* free RAM buffer with last bcode part */ |
||
10200 |
free((void *) bp->data.p); |
||
10201 |
|||
10202 |
/* mmap .jsc file and set last bcode part buffer to it */ |
||
10203 |
bp->data.p = cs_mmap_file(filename_jsc, &bp->data.len); |
||
10204 |
bp->in_rom = 1; |
||
10205 |
} |
||
10206 |
} |
||
10207 |
} |
||
10208 |
#else |
||
10209 |
(void) generate_jsc; |
||
10210 |
#endif |
||
10211 |
|||
10212 |
66 |
mjs_execute(mjs, off, &r); |
|
10213 |
} else { |
||
10214 |
12 |
return mjs->error; |
|
10215 |
} |
||
10216 |
✓✗ | 66 |
if (res != NULL) *res = r; |
10217 |
66 |
return MJS_OK; |
|
10218 |
// return mjs->error; |
||
10219 |
} |
||
10220 |
|||
10221 |
78 |
mjs_err_t mjs_exec(struct mjs *mjs, const char *src, mjs_val_t *res) { |
|
10222 |
78 |
return mjs_exec_internal(mjs, "<stdin>", src, 0 /* generate_jsc */, res); |
|
10223 |
} |
||
10224 |
|||
10225 |
mjs_err_t mjs_exec_file(struct mjs *mjs, const char *path, mjs_val_t *res) { |
||
10226 |
mjs_err_t error = MJS_FILE_READ_ERROR; |
||
10227 |
mjs_val_t r = MJS_UNDEFINED; |
||
10228 |
size_t size; |
||
10229 |
char *source_code = cs_read_file(path, &size); |
||
10230 |
|||
10231 |
if (source_code == NULL) { |
||
10232 |
error = MJS_FILE_READ_ERROR; |
||
10233 |
mjs_prepend_errorf(mjs, error, "failed to read file \"%s\"", path); |
||
10234 |
goto clean; |
||
10235 |
} |
||
10236 |
|||
10237 |
r = MJS_UNDEFINED; |
||
10238 |
error = mjs_exec_internal(mjs, path, source_code, -1, &r); |
||
10239 |
free(source_code); |
||
10240 |
|||
10241 |
clean: |
||
10242 |
if (res != NULL) *res = r; |
||
10243 |
return error; |
||
10244 |
} |
||
10245 |
|||
10246 |
mjs_err_t mjs_call(struct mjs *mjs, mjs_val_t *res, mjs_val_t func, |
||
10247 |
mjs_val_t this_val, int nargs, ...) { |
||
10248 |
va_list ap; |
||
10249 |
int i; |
||
10250 |
mjs_err_t ret; |
||
10251 |
mjs_val_t *args = calloc(nargs, sizeof(mjs_val_t)); |
||
10252 |
va_start(ap, nargs); |
||
10253 |
for (i = 0; i < nargs; i++) { |
||
10254 |
args[i] = va_arg(ap, mjs_val_t); |
||
10255 |
} |
||
10256 |
va_end(ap); |
||
10257 |
|||
10258 |
ret = mjs_apply(mjs, res, func, this_val, nargs, args); |
||
10259 |
|||
10260 |
free(args); |
||
10261 |
return ret; |
||
10262 |
} |
||
10263 |
|||
10264 |
mjs_err_t mjs_apply(struct mjs *mjs, mjs_val_t *res, mjs_val_t func, |
||
10265 |
mjs_val_t this_val, int nargs, mjs_val_t *args) { |
||
10266 |
mjs_val_t r, prev_this_val, retval_stack_idx, *resp; |
||
10267 |
int i; |
||
10268 |
|||
10269 |
if (!mjs_is_function(func) && !mjs_is_foreign(func) && |
||
10270 |
!mjs_is_ffi_sig(func)) { |
||
10271 |
return mjs_set_errorf(mjs, MJS_TYPE_ERROR, "calling non-callable"); |
||
10272 |
} |
||
10273 |
|||
10274 |
LOG(LL_VERBOSE_DEBUG, ("applying func %d", (int) mjs_get_func_addr(func))); |
||
10275 |
|||
10276 |
prev_this_val = mjs->vals.this_obj; |
||
10277 |
|||
10278 |
/* Push callable which will be later replaced with the return value */ |
||
10279 |
mjs_push(mjs, func); |
||
10280 |
resp = vptr(&mjs->stack, -1); |
||
10281 |
|||
10282 |
/* Remember index by which return value should be written */ |
||
10283 |
retval_stack_idx = mjs_mk_number(mjs, (double) mjs_stack_size(&mjs->stack)); |
||
10284 |
|||
10285 |
// Push all arguments |
||
10286 |
for (i = 0; i < nargs; i++) { |
||
10287 |
mjs_push(mjs, args[i]); |
||
10288 |
} |
||
10289 |
|||
10290 |
/* Push this value to arg_stack, call_stack_push_frame() expects that */ |
||
10291 |
push_mjs_val(&mjs->arg_stack, this_val); |
||
10292 |
|||
10293 |
/* Push call stack frame, just like OP_CALL does that */ |
||
10294 |
call_stack_push_frame(mjs, MJS_BCODE_OFFSET_EXIT, retval_stack_idx); |
||
10295 |
|||
10296 |
if (mjs_is_foreign(func)) { |
||
10297 |
((void (*) (struct mjs *)) mjs_get_ptr(mjs, func))(mjs); |
||
10298 |
if (res != NULL) *res = *resp; |
||
10299 |
} else if (mjs_is_ffi_sig(func)) { |
||
10300 |
mjs_ffi_call2(mjs); |
||
10301 |
if (res != NULL) *res = *resp; |
||
10302 |
} else { |
||
10303 |
size_t addr = mjs_get_func_addr(func); |
||
10304 |
mjs_execute(mjs, addr, &r); |
||
10305 |
if (res != NULL) *res = r; |
||
10306 |
} |
||
10307 |
|||
10308 |
/* |
||
10309 |
* If there was an error, we need to restore frame and do the cleanup |
||
10310 |
* which is otherwise done by OP_RETURN |
||
10311 |
*/ |
||
10312 |
if (mjs->error != MJS_OK) { |
||
10313 |
call_stack_restore_frame(mjs); |
||
10314 |
|||
10315 |
// Pop cell at which the returned value should've been written |
||
10316 |
mjs_pop(mjs); |
||
10317 |
} |
||
10318 |
mjs->vals.this_obj = prev_this_val; |
||
10319 |
|||
10320 |
return mjs->error; |
||
10321 |
} |
||
10322 |
#ifdef MJS_MODULE_LINES |
||
10323 |
#line 1 "mjs/src/mjs_ffi.c" |
||
10324 |
#endif |
||
10325 |
/* |
||
10326 |
* Copyright (c) 2017 Cesanta Software Limited |
||
10327 |
* All rights reserved |
||
10328 |
*/ |
||
10329 |
|||
10330 |
/* Amalgamated: #include "common/mg_str.h" */ |
||
10331 |
|||
10332 |
/* Amalgamated: #include "mjs/src/ffi/ffi.h" */ |
||
10333 |
/* Amalgamated: #include "mjs/src/mjs_core.h" */ |
||
10334 |
/* Amalgamated: #include "mjs/src/mjs_exec.h" */ |
||
10335 |
/* Amalgamated: #include "mjs/src/mjs_internal.h" */ |
||
10336 |
/* Amalgamated: #include "mjs/src/mjs_primitive.h" */ |
||
10337 |
/* Amalgamated: #include "mjs/src/mjs_string.h" */ |
||
10338 |
/* Amalgamated: #include "mjs/src/mjs_util.h" */ |
||
10339 |
|||
10340 |
/* |
||
10341 |
* on linux this is enabled only if __USE_GNU is defined, but we cannot set it |
||
10342 |
* because dlfcn could have been included already. |
||
10343 |
*/ |
||
10344 |
#ifndef RTLD_DEFAULT |
||
10345 |
#define RTLD_DEFAULT NULL |
||
10346 |
#endif |
||
10347 |
|||
10348 |
static ffi_fn_t *get_cb_impl_by_signature(const mjs_ffi_sig_t *sig); |
||
10349 |
|||
10350 |
/* |
||
10351 |
* Data of the two related arguments: callback function pointer and the |
||
10352 |
* userdata for it |
||
10353 |
*/ |
||
10354 |
struct cbdata { |
||
10355 |
/* JS callback function */ |
||
10356 |
mjs_val_t func; |
||
10357 |
/* JS userdata */ |
||
10358 |
mjs_val_t userdata; |
||
10359 |
|||
10360 |
/* index of the function pointer param */ |
||
10361 |
int8_t func_idx; |
||
10362 |
/* index of the userdata param */ |
||
10363 |
int8_t userdata_idx; |
||
10364 |
}; |
||
10365 |
|||
10366 |
78 |
void mjs_set_ffi_resolver(struct mjs *mjs, mjs_ffi_resolver_t *dlsym) { |
|
10367 |
78 |
mjs->dlsym = dlsym; |
|
10368 |
78 |
} |
|
10369 |
|||
10370 |
static mjs_ffi_ctype_t parse_cval_type(struct mjs *mjs, const char *s, |
||
10371 |
const char *e) { |
||
10372 |
struct mg_str ms = MG_NULL_STR; |
||
10373 |
/* Trim leading and trailing whitespace */ |
||
10374 |
while (s < e && isspace((int) *s)) s++; |
||
10375 |
while (e > s && isspace((int) e[-1])) e--; |
||
10376 |
ms.p = s; |
||
10377 |
ms.len = e - s; |
||
10378 |
if (mg_vcmp(&ms, "void") == 0) { |
||
10379 |
return MJS_FFI_CTYPE_NONE; |
||
10380 |
} else if (mg_vcmp(&ms, "userdata") == 0) { |
||
10381 |
return MJS_FFI_CTYPE_USERDATA; |
||
10382 |
} else if (mg_vcmp(&ms, "int") == 0) { |
||
10383 |
return MJS_FFI_CTYPE_INT; |
||
10384 |
} else if (mg_vcmp(&ms, "bool") == 0) { |
||
10385 |
return MJS_FFI_CTYPE_BOOL; |
||
10386 |
} else if (mg_vcmp(&ms, "double") == 0) { |
||
10387 |
return MJS_FFI_CTYPE_DOUBLE; |
||
10388 |
} else if (mg_vcmp(&ms, "float") == 0) { |
||
10389 |
return MJS_FFI_CTYPE_FLOAT; |
||
10390 |
} else if (mg_vcmp(&ms, "char*") == 0 || mg_vcmp(&ms, "char *") == 0) { |
||
10391 |
return MJS_FFI_CTYPE_CHAR_PTR; |
||
10392 |
} else if (mg_vcmp(&ms, "void*") == 0 || mg_vcmp(&ms, "void *") == 0) { |
||
10393 |
return MJS_FFI_CTYPE_VOID_PTR; |
||
10394 |
} else if (mg_vcmp(&ms, "struct mg_str") == 0) { |
||
10395 |
return MJS_FFI_CTYPE_STRUCT_MG_STR; |
||
10396 |
} else if (mg_vcmp(&ms, "struct mg_str *") == 0 || |
||
10397 |
mg_vcmp(&ms, "struct mg_str*") == 0) { |
||
10398 |
return MJS_FFI_CTYPE_STRUCT_MG_STR_PTR; |
||
10399 |
} else { |
||
10400 |
mjs_prepend_errorf(mjs, MJS_TYPE_ERROR, "failed to parse val type \"%.*s\"", |
||
10401 |
(int) ms.len, ms.p); |
||
10402 |
return MJS_FFI_CTYPE_INVALID; |
||
10403 |
} |
||
10404 |
} |
||
10405 |
|||
10406 |
static const char *find_paren(const char *s, const char *e) { |
||
10407 |
for (; s < e; s++) { |
||
10408 |
if (*s == '(') return s; |
||
10409 |
} |
||
10410 |
return NULL; |
||
10411 |
} |
||
10412 |
|||
10413 |
static const char *find_closing_paren(const char *s, const char *e) { |
||
10414 |
int nesting = 1; |
||
10415 |
while (s < e) { |
||
10416 |
if (*s == '(') { |
||
10417 |
nesting++; |
||
10418 |
} else if (*s == ')') { |
||
10419 |
if (--nesting == 0) break; |
||
10420 |
} |
||
10421 |
s++; |
||
10422 |
} |
||
10423 |
return (s < e ? s : NULL); |
||
10424 |
} |
||
10425 |
|||
10426 |
MJS_PRIVATE mjs_err_t mjs_parse_ffi_signature(struct mjs *mjs, const char *s, |
||
10427 |
int sig_len, mjs_ffi_sig_t *sig, |
||
10428 |
enum ffi_sig_type sig_type) { |
||
10429 |
mjs_err_t ret = MJS_OK; |
||
10430 |
int vtidx = 0; |
||
10431 |
const char *cur, *e, *tmp_e, *tmp; |
||
10432 |
struct mg_str rt = MG_NULL_STR, fn = MG_NULL_STR, args = MG_NULL_STR; |
||
10433 |
mjs_ffi_ctype_t val_type = MJS_FFI_CTYPE_INVALID; |
||
10434 |
if (sig_len == ~0) { |
||
10435 |
sig_len = strlen(s); |
||
10436 |
} |
||
10437 |
e = s + sig_len; |
||
10438 |
|||
10439 |
mjs_ffi_sig_init(sig); |
||
10440 |
|||
10441 |
/* Skip leading spaces */ |
||
10442 |
for (cur = s; cur < e && isspace((int) *cur); cur++) |
||
10443 |
; |
||
10444 |
|||
10445 |
/* FInd the first set of parens */ |
||
10446 |
tmp_e = find_paren(cur, e); |
||
10447 |
if (tmp_e == NULL || tmp_e - s < 2) { |
||
10448 |
ret = MJS_TYPE_ERROR; |
||
10449 |
mjs_prepend_errorf(mjs, ret, "1"); |
||
10450 |
goto clean; |
||
10451 |
} |
||
10452 |
tmp = find_closing_paren(tmp_e + 1, e); |
||
10453 |
if (tmp == NULL) { |
||
10454 |
ret = MJS_TYPE_ERROR; |
||
10455 |
mjs_prepend_errorf(mjs, ret, "2"); |
||
10456 |
goto clean; |
||
10457 |
} |
||
10458 |
|||
10459 |
/* Now see if we have a second set of parens */ |
||
10460 |
args.p = find_paren(tmp + 1, e); |
||
10461 |
if (args.p == NULL) { |
||
10462 |
/* We don't - it's a regular function signature */ |
||
10463 |
fn.p = tmp_e - 1; |
||
10464 |
while (fn.p > cur && isspace((int) *fn.p)) fn.p--; |
||
10465 |
while (fn.p > cur && (isalnum((int) *fn.p) || *fn.p == '_')) { |
||
10466 |
fn.p--; |
||
10467 |
fn.len++; |
||
10468 |
} |
||
10469 |
fn.p++; |
||
10470 |
rt.p = cur; |
||
10471 |
rt.len = fn.p - rt.p; |
||
10472 |
/* Stuff inside parens is args */ |
||
10473 |
args.p = tmp_e + 1; |
||
10474 |
args.len = tmp - args.p; |
||
10475 |
} else { |
||
10476 |
/* We do - it's a function pointer, like void (*foo)(...). |
||
10477 |
* Stuff inside the first pair of parens is the function name */ |
||
10478 |
fn.p = tmp + 1; |
||
10479 |
fn.len = args.p - tmp; |
||
10480 |
rt.p = cur; |
||
10481 |
rt.len = tmp_e - rt.p; |
||
10482 |
args.p++; |
||
10483 |
tmp = find_closing_paren(args.p, e); |
||
10484 |
if (tmp == NULL) { |
||
10485 |
ret = MJS_TYPE_ERROR; |
||
10486 |
mjs_prepend_errorf(mjs, ret, "3"); |
||
10487 |
goto clean; |
||
10488 |
} |
||
10489 |
args.len = tmp - args.p; |
||
10490 |
/* |
||
10491 |
* We ignore the name and leave sig->fn NULL here, but it will later be |
||
10492 |
* set to the appropriate callback implementation. |
||
10493 |
*/ |
||
10494 |
sig->is_callback = 1; |
||
10495 |
} |
||
10496 |
|||
10497 |
val_type = parse_cval_type(mjs, rt.p, rt.p + rt.len); |
||
10498 |
if (val_type == MJS_FFI_CTYPE_INVALID) { |
||
10499 |
ret = mjs->error; |
||
10500 |
goto clean; |
||
10501 |
} |
||
10502 |
mjs_ffi_sig_set_val_type(sig, vtidx++, val_type); |
||
10503 |
|||
10504 |
/* Parse function name {{{ */ |
||
10505 |
if (!sig->is_callback) { |
||
10506 |
char buf[100]; |
||
10507 |
if (mjs->dlsym == NULL) { |
||
10508 |
ret = MJS_TYPE_ERROR; |
||
10509 |
mjs_prepend_errorf(mjs, ret, |
||
10510 |
"resolver is not set, call mjs_set_ffi_resolver"); |
||
10511 |
goto clean; |
||
10512 |
} |
||
10513 |
|||
10514 |
snprintf(buf, sizeof(buf), "%.*s", (int) fn.len, fn.p); |
||
10515 |
sig->fn = (ffi_fn_t *) mjs->dlsym(RTLD_DEFAULT, buf); |
||
10516 |
if (sig->fn == NULL) { |
||
10517 |
ret = MJS_TYPE_ERROR; |
||
10518 |
mjs_prepend_errorf(mjs, ret, "dlsym('%s') failed", buf); |
||
10519 |
goto clean; |
||
10520 |
} |
||
10521 |
} else { |
||
10522 |
tmp_e = strchr(tmp_e, ')'); |
||
10523 |
if (tmp_e == NULL) { |
||
10524 |
ret = MJS_TYPE_ERROR; |
||
10525 |
goto clean; |
||
10526 |
} |
||
10527 |
} |
||
10528 |
|||
10529 |
/* Advance cur to the beginning of the arg list */ |
||
10530 |
cur = tmp_e = args.p; |
||
10531 |
|||
10532 |
/* Parse all args {{{ */ |
||
10533 |
while (tmp_e - args.p < (ptrdiff_t) args.len) { |
||
10534 |
int level = 0; /* nested parens level */ |
||
10535 |
int is_fp = 0; /* set to 1 is current arg is a callback function ptr */ |
||
10536 |
tmp_e = cur; |
||
10537 |
|||
10538 |
/* Advance tmp_e until the next arg separator */ |
||
10539 |
while (*tmp_e && (level > 0 || (*tmp_e != ',' && *tmp_e != ')'))) { |
||
10540 |
switch (*tmp_e) { |
||
10541 |
case '(': |
||
10542 |
level++; |
||
10543 |
/* |
||
10544 |
* only function pointer params can have parens, so, set the flag |
||
10545 |
* that it's going to be a function pointer |
||
10546 |
*/ |
||
10547 |
is_fp = 1; |
||
10548 |
break; |
||
10549 |
case ')': |
||
10550 |
level--; |
||
10551 |
break; |
||
10552 |
} |
||
10553 |
tmp_e++; |
||
10554 |
} |
||
10555 |
|||
10556 |
if (tmp_e == cur) break; |
||
10557 |
|||
10558 |
/* Parse current arg */ |
||
10559 |
if (is_fp) { |
||
10560 |
/* Current argument is a callback function pointer */ |
||
10561 |
if (sig->cb_sig != NULL) { |
||
10562 |
/* |
||
10563 |
* We already have parsed some callback argument. Currently we don't |
||
10564 |
* support more than one callback argument, so, return error |
||
10565 |
* TODO(dfrank): probably improve |
||
10566 |
*/ |
||
10567 |
ret = MJS_TYPE_ERROR; |
||
10568 |
mjs_prepend_errorf(mjs, ret, "only one callback is allowed"); |
||
10569 |
goto clean; |
||
10570 |
} |
||
10571 |
|||
10572 |
sig->cb_sig = calloc(sizeof(*sig->cb_sig), 1); |
||
10573 |
ret = mjs_parse_ffi_signature(mjs, cur, tmp_e - cur, sig->cb_sig, |
||
10574 |
FFI_SIG_CALLBACK); |
||
10575 |
if (ret != MJS_OK) { |
||
10576 |
mjs_ffi_sig_free(sig->cb_sig); |
||
10577 |
free(sig->cb_sig); |
||
10578 |
sig->cb_sig = NULL; |
||
10579 |
goto clean; |
||
10580 |
} |
||
10581 |
val_type = MJS_FFI_CTYPE_CALLBACK; |
||
10582 |
} else { |
||
10583 |
/* Some non-function argument */ |
||
10584 |
val_type = parse_cval_type(mjs, cur, tmp_e); |
||
10585 |
if (val_type == MJS_FFI_CTYPE_INVALID) { |
||
10586 |
/* parse_cval_type() has already set error message */ |
||
10587 |
ret = MJS_TYPE_ERROR; |
||
10588 |
goto clean; |
||
10589 |
} |
||
10590 |
} |
||
10591 |
|||
10592 |
if (!mjs_ffi_sig_set_val_type(sig, vtidx++, val_type)) { |
||
10593 |
ret = MJS_TYPE_ERROR; |
||
10594 |
mjs_prepend_errorf(mjs, ret, "too many callback args"); |
||
10595 |
goto clean; |
||
10596 |
} |
||
10597 |
|||
10598 |
if (*tmp_e == ',') { |
||
10599 |
/* Advance cur to the next argument */ |
||
10600 |
cur = tmp_e + 1; |
||
10601 |
while (*cur == ' ') cur++; |
||
10602 |
} else { |
||
10603 |
/* No more arguments */ |
||
10604 |
break; |
||
10605 |
} |
||
10606 |
} |
||
10607 |
/* }}} */ |
||
10608 |
|||
10609 |
/* Analyze the results and see if they are obviously wrong */ |
||
10610 |
mjs_ffi_sig_validate(mjs, sig, sig_type); |
||
10611 |
if (!sig->is_valid) { |
||
10612 |
ret = MJS_TYPE_ERROR; |
||
10613 |
goto clean; |
||
10614 |
} |
||
10615 |
|||
10616 |
/* If the signature represents a callback, find the suitable implementation */ |
||
10617 |
if (sig->is_callback) { |
||
10618 |
sig->fn = get_cb_impl_by_signature(sig); |
||
10619 |
if (sig->fn == NULL) { |
||
10620 |
ret = MJS_TYPE_ERROR; |
||
10621 |
mjs_prepend_errorf(mjs, ret, |
||
10622 |
"the callback signature is valid, but there's " |
||
10623 |
"no existing callback implementation for it"); |
||
10624 |
goto clean; |
||
10625 |
} |
||
10626 |
} |
||
10627 |
|||
10628 |
clean: |
||
10629 |
if (ret != MJS_OK) { |
||
10630 |
mjs_prepend_errorf(mjs, ret, "bad ffi signature: \"%.*s\"", sig_len, s); |
||
10631 |
sig->is_valid = 0; |
||
10632 |
} |
||
10633 |
return ret; |
||
10634 |
} |
||
10635 |
|||
10636 |
/* C callbacks implementation {{{ */ |
||
10637 |
|||
10638 |
/* An argument or a return value for C callback impl */ |
||
10639 |
union ffi_cb_data_val { |
||
10640 |
void *p; |
||
10641 |
uintptr_t w; |
||
10642 |
double d; |
||
10643 |
float f; |
||
10644 |
}; |
||
10645 |
|||
10646 |
struct ffi_cb_data { |
||
10647 |
union ffi_cb_data_val args[MJS_CB_ARGS_MAX_CNT]; |
||
10648 |
}; |
||
10649 |
|||
10650 |
static union ffi_cb_data_val ffi_cb_impl_generic(void *param, |
||
10651 |
struct ffi_cb_data *data) { |
||
10652 |
struct mjs_ffi_cb_args *cbargs = (struct mjs_ffi_cb_args *) param; |
||
10653 |
mjs_val_t *args, res = MJS_UNDEFINED; |
||
10654 |
union ffi_cb_data_val ret; |
||
10655 |
int i; |
||
10656 |
struct mjs *mjs = cbargs->mjs; |
||
10657 |
mjs_ffi_ctype_t return_ctype = MJS_FFI_CTYPE_NONE; |
||
10658 |
mjs_err_t err; |
||
10659 |
|||
10660 |
memset(&ret, 0, sizeof(ret)); |
||
10661 |
mjs_own(mjs, &res); |
||
10662 |
|||
10663 |
/* There must be at least one argument: a userdata */ |
||
10664 |
assert(cbargs->sig.args_cnt > 0); |
||
10665 |
|||
10666 |
/* Create JS arguments */ |
||
10667 |
args = calloc(1, sizeof(mjs_val_t) * cbargs->sig.args_cnt); |
||
10668 |
for (i = 0; i < cbargs->sig.args_cnt; i++) { |
||
10669 |
mjs_ffi_ctype_t val_type = |
||
10670 |
cbargs->sig.val_types[i + 1 /* first val_type is return value type */]; |
||
10671 |
switch (val_type) { |
||
10672 |
case MJS_FFI_CTYPE_USERDATA: |
||
10673 |
args[i] = cbargs->userdata; |
||
10674 |
break; |
||
10675 |
case MJS_FFI_CTYPE_INT: |
||
10676 |
args[i] = mjs_mk_number(mjs, (double) data->args[i].w); |
||
10677 |
break; |
||
10678 |
case MJS_FFI_CTYPE_BOOL: |
||
10679 |
args[i] = mjs_mk_boolean(mjs, !!data->args[i].w); |
||
10680 |
break; |
||
10681 |
case MJS_FFI_CTYPE_CHAR_PTR: { |
||
10682 |
const char *s = (char *) data->args[i].w; |
||
10683 |
if (s == NULL) s = ""; |
||
10684 |
args[i] = mjs_mk_string(mjs, s, ~0, 1); |
||
10685 |
break; |
||
10686 |
} |
||
10687 |
case MJS_FFI_CTYPE_VOID_PTR: |
||
10688 |
args[i] = mjs_mk_foreign(mjs, (void *) data->args[i].w); |
||
10689 |
break; |
||
10690 |
case MJS_FFI_CTYPE_DOUBLE: |
||
10691 |
args[i] = mjs_mk_number(mjs, data->args[i].d); |
||
10692 |
break; |
||
10693 |
case MJS_FFI_CTYPE_FLOAT: |
||
10694 |
args[i] = mjs_mk_number(mjs, data->args[i].f); |
||
10695 |
break; |
||
10696 |
case MJS_FFI_CTYPE_STRUCT_MG_STR_PTR: { |
||
10697 |
struct mg_str *s = (struct mg_str *) (void *) data->args[i].w; |
||
10698 |
args[i] = mjs_mk_string(mjs, s->p, s->len, 1); |
||
10699 |
break; |
||
10700 |
} |
||
10701 |
default: |
||
10702 |
/* should never be here */ |
||
10703 |
LOG(LL_ERROR, ("unexpected val type for arg #%d: %d\n", i, val_type)); |
||
10704 |
abort(); |
||
10705 |
} |
||
10706 |
} |
||
10707 |
|||
10708 |
/* |
||
10709 |
* save return ctype outside of `cbargs` before calling the callback, because |
||
10710 |
* callback might call `ffi_cb_free()`, which will effectively invalidate |
||
10711 |
* `cbargs` |
||
10712 |
*/ |
||
10713 |
return_ctype = cbargs->sig.val_types[0]; |
||
10714 |
|||
10715 |
/* Call JS function */ |
||
10716 |
LOG(LL_VERBOSE_DEBUG, ("calling JS callback void-void %d from C", |
||
10717 |
mjs_get_int(mjs, cbargs->func))); |
||
10718 |
err = mjs_apply(mjs, &res, cbargs->func, MJS_UNDEFINED, cbargs->sig.args_cnt, |
||
10719 |
args); |
||
10720 |
/* |
||
10721 |
* cbargs might be invalidated by the callback (if it called ffi_cb_free), so |
||
10722 |
* null it out |
||
10723 |
*/ |
||
10724 |
cbargs = NULL; |
||
10725 |
if (err != MJS_OK) { |
||
10726 |
/* |
||
10727 |
* There's not much we can do about the error here; let's at least print it |
||
10728 |
*/ |
||
10729 |
mjs_print_error(mjs, stderr, "MJS callback error", |
||
10730 |
1 /* print_stack_trace */); |
||
10731 |
|||
10732 |
goto clean; |
||
10733 |
} |
||
10734 |
|||
10735 |
/* Get return value, if needed */ |
||
10736 |
switch (return_ctype) { |
||
10737 |
case MJS_FFI_CTYPE_NONE: |
||
10738 |
/* do nothing */ |
||
10739 |
break; |
||
10740 |
case MJS_FFI_CTYPE_INT: |
||
10741 |
ret.w = mjs_get_int(mjs, res); |
||
10742 |
break; |
||
10743 |
case MJS_FFI_CTYPE_BOOL: |
||
10744 |
ret.w = mjs_get_bool(mjs, res); |
||
10745 |
break; |
||
10746 |
case MJS_FFI_CTYPE_VOID_PTR: |
||
10747 |
ret.p = mjs_get_ptr(mjs, res); |
||
10748 |
break; |
||
10749 |
case MJS_FFI_CTYPE_DOUBLE: |
||
10750 |
ret.d = mjs_get_double(mjs, res); |
||
10751 |
break; |
||
10752 |
case MJS_FFI_CTYPE_FLOAT: |
||
10753 |
ret.f = (float) mjs_get_double(mjs, res); |
||
10754 |
break; |
||
10755 |
default: |
||
10756 |
/* should never be here */ |
||
10757 |
LOG(LL_ERROR, ("unexpected return val type %d\n", return_ctype)); |
||
10758 |
abort(); |
||
10759 |
} |
||
10760 |
|||
10761 |
clean: |
||
10762 |
free(args); |
||
10763 |
mjs_disown(mjs, &res); |
||
10764 |
return ret; |
||
10765 |
} |
||
10766 |
|||
10767 |
static void ffi_init_cb_data_wwww(struct ffi_cb_data *data, uintptr_t w0, |
||
10768 |
uintptr_t w1, uintptr_t w2, uintptr_t w3, |
||
10769 |
uintptr_t w4, uintptr_t w5) { |
||
10770 |
memset(data, 0, sizeof(*data)); |
||
10771 |
data->args[0].w = w0; |
||
10772 |
data->args[1].w = w1; |
||
10773 |
data->args[2].w = w2; |
||
10774 |
data->args[3].w = w3; |
||
10775 |
data->args[4].w = w4; |
||
10776 |
data->args[5].w = w5; |
||
10777 |
} |
||
10778 |
|||
10779 |
static uintptr_t ffi_cb_impl_wpwwwww(uintptr_t w0, uintptr_t w1, uintptr_t w2, |
||
10780 |
uintptr_t w3, uintptr_t w4, uintptr_t w5) { |
||
10781 |
struct ffi_cb_data data; |
||
10782 |
ffi_init_cb_data_wwww(&data, w0, w1, w2, w3, w4, w5); |
||
10783 |
return ffi_cb_impl_generic((void *) w0, &data).w; |
||
10784 |
} |
||
10785 |
|||
10786 |
static uintptr_t ffi_cb_impl_wwpwwww(uintptr_t w0, uintptr_t w1, uintptr_t w2, |
||
10787 |
uintptr_t w3, uintptr_t w4, uintptr_t w5) { |
||
10788 |
struct ffi_cb_data data; |
||
10789 |
ffi_init_cb_data_wwww(&data, w0, w1, w2, w3, w4, w5); |
||
10790 |
return ffi_cb_impl_generic((void *) w1, &data).w; |
||
10791 |
} |
||
10792 |
|||
10793 |
static uintptr_t ffi_cb_impl_wwwpwww(uintptr_t w0, uintptr_t w1, uintptr_t w2, |
||
10794 |
uintptr_t w3, uintptr_t w4, uintptr_t w5) { |
||
10795 |
struct ffi_cb_data data; |
||
10796 |
ffi_init_cb_data_wwww(&data, w0, w1, w2, w3, w4, w5); |
||
10797 |
return ffi_cb_impl_generic((void *) w2, &data).w; |
||
10798 |
} |
||
10799 |
|||
10800 |
static uintptr_t ffi_cb_impl_wwwwpww(uintptr_t w0, uintptr_t w1, uintptr_t w2, |
||
10801 |
uintptr_t w3, uintptr_t w4, uintptr_t w5) { |
||
10802 |
struct ffi_cb_data data; |
||
10803 |
ffi_init_cb_data_wwww(&data, w0, w1, w2, w3, w4, w5); |
||
10804 |
return ffi_cb_impl_generic((void *) w3, &data).w; |
||
10805 |
} |
||
10806 |
|||
10807 |
static uintptr_t ffi_cb_impl_wwwwwpw(uintptr_t w0, uintptr_t w1, uintptr_t w2, |
||
10808 |
uintptr_t w3, uintptr_t w4, uintptr_t w5) { |
||
10809 |
struct ffi_cb_data data; |
||
10810 |
ffi_init_cb_data_wwww(&data, w0, w1, w2, w3, w4, w5); |
||
10811 |
return ffi_cb_impl_generic((void *) w4, &data).w; |
||
10812 |
} |
||
10813 |
|||
10814 |
static uintptr_t ffi_cb_impl_wwwwwwp(uintptr_t w0, uintptr_t w1, uintptr_t w2, |
||
10815 |
uintptr_t w3, uintptr_t w4, uintptr_t w5) { |
||
10816 |
struct ffi_cb_data data; |
||
10817 |
ffi_init_cb_data_wwww(&data, w0, w1, w2, w3, w4, w5); |
||
10818 |
return ffi_cb_impl_generic((void *) w5, &data).w; |
||
10819 |
} |
||
10820 |
|||
10821 |
static uintptr_t ffi_cb_impl_wpd(uintptr_t w0, double d1) { |
||
10822 |
struct ffi_cb_data data; |
||
10823 |
|||
10824 |
memset(&data, 0, sizeof(data)); |
||
10825 |
data.args[0].w = w0; |
||
10826 |
data.args[1].d = d1; |
||
10827 |
|||
10828 |
return ffi_cb_impl_generic((void *) w0, &data).w; |
||
10829 |
} |
||
10830 |
|||
10831 |
static uintptr_t ffi_cb_impl_wdp(double d0, uintptr_t w1) { |
||
10832 |
struct ffi_cb_data data; |
||
10833 |
|||
10834 |
memset(&data, 0, sizeof(data)); |
||
10835 |
data.args[0].d = d0; |
||
10836 |
data.args[1].w = w1; |
||
10837 |
|||
10838 |
return ffi_cb_impl_generic((void *) w1, &data).w; |
||
10839 |
} |
||
10840 |
/* }}} */ |
||
10841 |
|||
10842 |
static struct mjs_ffi_cb_args **ffi_get_matching(struct mjs_ffi_cb_args **plist, |
||
10843 |
mjs_val_t func, |
||
10844 |
mjs_val_t userdata) { |
||
10845 |
for (; *plist != NULL; plist = &((*plist)->next)) { |
||
10846 |
if ((*plist)->func == func && (*plist)->userdata == userdata) { |
||
10847 |
break; |
||
10848 |
} |
||
10849 |
} |
||
10850 |
return plist; |
||
10851 |
} |
||
10852 |
|||
10853 |
static ffi_fn_t *get_cb_impl_by_signature(const mjs_ffi_sig_t *sig) { |
||
10854 |
if (sig->is_valid) { |
||
10855 |
int i; |
||
10856 |
int double_cnt = 0; |
||
10857 |
int float_cnt = 0; |
||
10858 |
int userdata_idx = 0 /* not a valid value: index 0 means return value */; |
||
10859 |
|||
10860 |
for (i = 1 /*0th item is a return value*/; i < MJS_CB_SIGNATURE_MAX_SIZE; |
||
10861 |
i++) { |
||
10862 |
mjs_ffi_ctype_t type = sig->val_types[i]; |
||
10863 |
switch (type) { |
||
10864 |
case MJS_FFI_CTYPE_DOUBLE: |
||
10865 |
double_cnt++; |
||
10866 |
break; |
||
10867 |
case MJS_FFI_CTYPE_FLOAT: |
||
10868 |
float_cnt++; |
||
10869 |
break; |
||
10870 |
case MJS_FFI_CTYPE_USERDATA: |
||
10871 |
assert(userdata_idx == 0); /* Otherwise is_valid should be 0 */ |
||
10872 |
userdata_idx = i; |
||
10873 |
break; |
||
10874 |
default: |
||
10875 |
break; |
||
10876 |
} |
||
10877 |
} |
||
10878 |
|||
10879 |
if (float_cnt > 0) { |
||
10880 |
/* TODO(dfrank): add support for floats in callbacks */ |
||
10881 |
return NULL; |
||
10882 |
} |
||
10883 |
|||
10884 |
assert(userdata_idx > 0); /* Otherwise is_valid should be 0 */ |
||
10885 |
|||
10886 |
if (sig->args_cnt <= MJS_CB_ARGS_MAX_CNT) { |
||
10887 |
if (mjs_ffi_is_regular_word_or_void(sig->val_types[0])) { |
||
10888 |
/* Return type is a word or void */ |
||
10889 |
switch (double_cnt) { |
||
10890 |
case 0: |
||
10891 |
/* No double arguments */ |
||
10892 |
switch (userdata_idx) { |
||
10893 |
case 1: |
||
10894 |
return (ffi_fn_t *) ffi_cb_impl_wpwwwww; |
||
10895 |
case 2: |
||
10896 |
return (ffi_fn_t *) ffi_cb_impl_wwpwwww; |
||
10897 |
case 3: |
||
10898 |
return (ffi_fn_t *) ffi_cb_impl_wwwpwww; |
||
10899 |
case 4: |
||
10900 |
return (ffi_fn_t *) ffi_cb_impl_wwwwpww; |
||
10901 |
case 5: |
||
10902 |
return (ffi_fn_t *) ffi_cb_impl_wwwwwpw; |
||
10903 |
case 6: |
||
10904 |
return (ffi_fn_t *) ffi_cb_impl_wwwwwwp; |
||
10905 |
default: |
||
10906 |
/* should never be here */ |
||
10907 |
abort(); |
||
10908 |
} |
||
10909 |
break; |
||
10910 |
case 1: |
||
10911 |
/* 1 double argument */ |
||
10912 |
switch (userdata_idx) { |
||
10913 |
case 1: |
||
10914 |
return (ffi_fn_t *) ffi_cb_impl_wpd; |
||
10915 |
case 2: |
||
10916 |
return (ffi_fn_t *) ffi_cb_impl_wdp; |
||
10917 |
} |
||
10918 |
break; |
||
10919 |
} |
||
10920 |
} |
||
10921 |
} else { |
||
10922 |
/* Too many arguments for the built-in callback impls */ |
||
10923 |
/* TODO(dfrank): add support for custom app-dependent resolver */ |
||
10924 |
} |
||
10925 |
} |
||
10926 |
|||
10927 |
return NULL; |
||
10928 |
} |
||
10929 |
|||
10930 |
MJS_PRIVATE mjs_val_t mjs_ffi_sig_to_value(struct mjs_ffi_sig *psig) { |
||
10931 |
if (psig == NULL) { |
||
10932 |
return MJS_NULL; |
||
10933 |
} else { |
||
10934 |
return mjs_legit_pointer_to_value(psig) | MJS_TAG_FUNCTION_FFI; |
||
10935 |
} |
||
10936 |
} |
||
10937 |
|||
10938 |
2682 |
MJS_PRIVATE int mjs_is_ffi_sig(mjs_val_t v) { |
|
10939 |
2682 |
return (v & MJS_TAG_MASK) == MJS_TAG_FUNCTION_FFI; |
|
10940 |
} |
||
10941 |
|||
10942 |
MJS_PRIVATE struct mjs_ffi_sig *mjs_get_ffi_sig_struct(mjs_val_t v) { |
||
10943 |
struct mjs_ffi_sig *ret = NULL; |
||
10944 |
assert(mjs_is_ffi_sig(v)); |
||
10945 |
ret = (struct mjs_ffi_sig *) get_ptr(v); |
||
10946 |
return ret; |
||
10947 |
} |
||
10948 |
|||
10949 |
MJS_PRIVATE mjs_val_t mjs_mk_ffi_sig(struct mjs *mjs) { |
||
10950 |
struct mjs_ffi_sig *psig = new_ffi_sig(mjs); |
||
10951 |
mjs_ffi_sig_init(psig); |
||
10952 |
return mjs_ffi_sig_to_value(psig); |
||
10953 |
} |
||
10954 |
|||
10955 |
MJS_PRIVATE void mjs_ffi_sig_destructor(struct mjs *mjs, void *psig) { |
||
10956 |
mjs_ffi_sig_free((mjs_ffi_sig_t *) psig); |
||
10957 |
(void) mjs; |
||
10958 |
} |
||
10959 |
|||
10960 |
MJS_PRIVATE mjs_err_t mjs_ffi_call(struct mjs *mjs) { |
||
10961 |
mjs_err_t e = MJS_OK; |
||
10962 |
const char *sig_str = NULL; |
||
10963 |
mjs_val_t sig_str_v = mjs_arg(mjs, 0); |
||
10964 |
mjs_val_t ret_v = MJS_UNDEFINED; |
||
10965 |
struct mjs_ffi_sig *psig = mjs_get_ffi_sig_struct(mjs_mk_ffi_sig(mjs)); |
||
10966 |
size_t sig_str_len; |
||
10967 |
|||
10968 |
sig_str = mjs_get_string(mjs, &sig_str_v, &sig_str_len); |
||
10969 |
e = mjs_parse_ffi_signature(mjs, sig_str, sig_str_len, psig, FFI_SIG_FUNC); |
||
10970 |
if (e != MJS_OK) goto clean; |
||
10971 |
ret_v = mjs_ffi_sig_to_value(psig); |
||
10972 |
|||
10973 |
clean: |
||
10974 |
mjs_return(mjs, ret_v); |
||
10975 |
return e; |
||
10976 |
} |
||
10977 |
|||
10978 |
MJS_PRIVATE mjs_err_t mjs_ffi_call2(struct mjs *mjs) { |
||
10979 |
mjs_err_t ret = MJS_OK; |
||
10980 |
mjs_ffi_sig_t *psig = NULL; |
||
10981 |
mjs_ffi_ctype_t rtype; |
||
10982 |
mjs_val_t sig_v = *vptr(&mjs->stack, mjs_getretvalpos(mjs)); |
||
10983 |
|||
10984 |
int i, nargs; |
||
10985 |
struct ffi_arg res; |
||
10986 |
struct ffi_arg args[FFI_MAX_ARGS_CNT]; |
||
10987 |
struct cbdata cbdata; |
||
10988 |
|||
10989 |
/* TODO(dfrank): support multiple callbacks */ |
||
10990 |
mjs_val_t resv = mjs_mk_undefined(); |
||
10991 |
|||
10992 |
/* |
||
10993 |
* String arguments, needed to support short strings which are packed into |
||
10994 |
* mjs_val_t itself |
||
10995 |
*/ |
||
10996 |
mjs_val_t argvs[FFI_MAX_ARGS_CNT]; |
||
10997 |
struct mg_str argvmgstr[FFI_MAX_ARGS_CNT]; |
||
10998 |
|||
10999 |
if (mjs_is_ffi_sig(sig_v)) { |
||
11000 |
psig = mjs_get_ffi_sig_struct(sig_v); |
||
11001 |
} else { |
||
11002 |
ret = MJS_TYPE_ERROR; |
||
11003 |
mjs_prepend_errorf(mjs, ret, "non-ffi-callable value"); |
||
11004 |
goto clean; |
||
11005 |
} |
||
11006 |
|||
11007 |
memset(&cbdata, 0, sizeof(cbdata)); |
||
11008 |
cbdata.func_idx = -1; |
||
11009 |
cbdata.userdata_idx = -1; |
||
11010 |
|||
11011 |
rtype = psig->val_types[0]; |
||
11012 |
|||
11013 |
switch (rtype) { |
||
11014 |
case MJS_FFI_CTYPE_DOUBLE: |
||
11015 |
res.ctype = FFI_CTYPE_DOUBLE; |
||
11016 |
break; |
||
11017 |
case MJS_FFI_CTYPE_FLOAT: |
||
11018 |
res.ctype = FFI_CTYPE_FLOAT; |
||
11019 |
break; |
||
11020 |
case MJS_FFI_CTYPE_BOOL: |
||
11021 |
res.ctype = FFI_CTYPE_BOOL; |
||
11022 |
break; |
||
11023 |
case MJS_FFI_CTYPE_USERDATA: |
||
11024 |
case MJS_FFI_CTYPE_INT: |
||
11025 |
case MJS_FFI_CTYPE_CHAR_PTR: |
||
11026 |
case MJS_FFI_CTYPE_VOID_PTR: |
||
11027 |
case MJS_FFI_CTYPE_NONE: |
||
11028 |
res.ctype = FFI_CTYPE_WORD; |
||
11029 |
break; |
||
11030 |
|||
11031 |
case MJS_FFI_CTYPE_INVALID: |
||
11032 |
ret = MJS_TYPE_ERROR; |
||
11033 |
mjs_prepend_errorf(mjs, ret, "wrong ffi return type"); |
||
11034 |
goto clean; |
||
11035 |
} |
||
11036 |
res.v.i = 0; |
||
11037 |
|||
11038 |
nargs = |
||
11039 |
mjs_stack_size(&mjs->stack) - mjs_get_int(mjs, vtop(&mjs->call_stack)); |
||
11040 |
|||
11041 |
if (nargs != psig->args_cnt) { |
||
11042 |
ret = MJS_TYPE_ERROR; |
||
11043 |
mjs_prepend_errorf(mjs, ret, "got %d actuals, but function takes %d args", |
||
11044 |
nargs, psig->args_cnt); |
||
11045 |
goto clean; |
||
11046 |
} |
||
11047 |
|||
11048 |
for (i = 0; i < nargs; i++) { |
||
11049 |
mjs_val_t arg = mjs_arg(mjs, i); |
||
11050 |
|||
11051 |
switch (psig->val_types[1 /* retval type */ + i]) { |
||
11052 |
case MJS_FFI_CTYPE_NONE: |
||
11053 |
/* |
||
11054 |
* Void argument: in any case, it's an error, because if C function |
||
11055 |
* takes no arguments, then the FFI-ed JS function should be called |
||
11056 |
* without any arguments, and thus we'll not face "void" here. |
||
11057 |
*/ |
||
11058 |
ret = MJS_TYPE_ERROR; |
||
11059 |
if (i == 0) { |
||
11060 |
/* FFI signature is correct, but invocation is wrong */ |
||
11061 |
mjs_prepend_errorf(mjs, ret, "ffi-ed function takes no arguments"); |
||
11062 |
} else { |
||
11063 |
/* |
||
11064 |
* FFI signature is wrong: we can't have "void" as a non-first |
||
11065 |
* "argument" |
||
11066 |
*/ |
||
11067 |
mjs_prepend_errorf(mjs, ret, "bad ffi arg #%d type: \"void\"", i); |
||
11068 |
} |
||
11069 |
|||
11070 |
goto clean; |
||
11071 |
case MJS_FFI_CTYPE_USERDATA: |
||
11072 |
/* Userdata for the callback */ |
||
11073 |
if (cbdata.userdata_idx != -1) { |
||
11074 |
ret = MJS_TYPE_ERROR; |
||
11075 |
mjs_prepend_errorf(mjs, ret, "two or more userdata args: #%d and %d", |
||
11076 |
cbdata.userdata_idx, i); |
||
11077 |
|||
11078 |
goto clean; |
||
11079 |
} |
||
11080 |
cbdata.userdata = arg; |
||
11081 |
cbdata.userdata_idx = i; |
||
11082 |
break; |
||
11083 |
case MJS_FFI_CTYPE_INT: { |
||
11084 |
int intval = 0; |
||
11085 |
if (mjs_is_number(arg)) { |
||
11086 |
intval = mjs_get_int(mjs, arg); |
||
11087 |
} else if (mjs_is_boolean(arg)) { |
||
11088 |
intval = mjs_get_bool(mjs, arg); |
||
11089 |
} else { |
||
11090 |
ret = MJS_TYPE_ERROR; |
||
11091 |
mjs_prepend_errorf( |
||
11092 |
mjs, ret, "actual arg #%d is not an int (the type idx is: %s)", i, |
||
11093 |
mjs_typeof(arg)); |
||
11094 |
} |
||
11095 |
ffi_set_word(&args[i], intval); |
||
11096 |
} break; |
||
11097 |
case MJS_FFI_CTYPE_STRUCT_MG_STR_PTR: { |
||
11098 |
if (!mjs_is_string(arg)) { |
||
11099 |
ret = MJS_TYPE_ERROR; |
||
11100 |
mjs_prepend_errorf( |
||
11101 |
mjs, ret, "actual arg #%d is not a string (the type idx is: %s)", |
||
11102 |
i, mjs_typeof(arg)); |
||
11103 |
goto clean; |
||
11104 |
} |
||
11105 |
argvs[i] = arg; |
||
11106 |
argvmgstr[i].p = mjs_get_string(mjs, &argvs[i], &argvmgstr[i].len); |
||
11107 |
/* |
||
11108 |
* String argument should be saved separately in order to support |
||
11109 |
* short strings (which are packed into mjs_val_t itself) |
||
11110 |
*/ |
||
11111 |
ffi_set_ptr(&args[i], (void *) &argvmgstr[i]); |
||
11112 |
break; |
||
11113 |
} |
||
11114 |
case MJS_FFI_CTYPE_BOOL: { |
||
11115 |
int intval = 0; |
||
11116 |
if (mjs_is_number(arg)) { |
||
11117 |
intval = !!mjs_get_int(mjs, arg); |
||
11118 |
} else if (mjs_is_boolean(arg)) { |
||
11119 |
intval = mjs_get_bool(mjs, arg); |
||
11120 |
} else { |
||
11121 |
ret = MJS_TYPE_ERROR; |
||
11122 |
mjs_prepend_errorf( |
||
11123 |
mjs, ret, "actual arg #%d is not a bool (the type idx is: %s)", i, |
||
11124 |
mjs_typeof(arg)); |
||
11125 |
} |
||
11126 |
ffi_set_word(&args[i], intval); |
||
11127 |
} break; |
||
11128 |
case MJS_FFI_CTYPE_DOUBLE: |
||
11129 |
ffi_set_double(&args[i], mjs_get_double(mjs, arg)); |
||
11130 |
break; |
||
11131 |
case MJS_FFI_CTYPE_FLOAT: |
||
11132 |
ffi_set_float(&args[i], (float) mjs_get_double(mjs, arg)); |
||
11133 |
break; |
||
11134 |
case MJS_FFI_CTYPE_CHAR_PTR: { |
||
11135 |
size_t s; |
||
11136 |
if (mjs_is_string(arg)) { |
||
11137 |
/* |
||
11138 |
* String argument should be saved separately in order to support |
||
11139 |
* short strings (which are packed into mjs_val_t itself) |
||
11140 |
*/ |
||
11141 |
argvs[i] = arg; |
||
11142 |
ffi_set_ptr(&args[i], (void *) mjs_get_string(mjs, &argvs[i], &s)); |
||
11143 |
} else if (mjs_is_null(arg)) { |
||
11144 |
ffi_set_ptr(&args[i], NULL); |
||
11145 |
} else { |
||
11146 |
ret = MJS_TYPE_ERROR; |
||
11147 |
mjs_prepend_errorf( |
||
11148 |
mjs, ret, "actual arg #%d is not a string (the type idx is: %s)", |
||
11149 |
i, mjs_typeof(arg)); |
||
11150 |
goto clean; |
||
11151 |
} |
||
11152 |
} break; |
||
11153 |
case MJS_FFI_CTYPE_VOID_PTR: |
||
11154 |
if (mjs_is_string(arg)) { |
||
11155 |
size_t n; |
||
11156 |
/* |
||
11157 |
* String argument should be saved separately in order to support |
||
11158 |
* short strings (which are packed into mjs_val_t itself) |
||
11159 |
*/ |
||
11160 |
argvs[i] = arg; |
||
11161 |
ffi_set_ptr(&args[i], (void *) mjs_get_string(mjs, &argvs[i], &n)); |
||
11162 |
} else if (mjs_is_foreign(arg)) { |
||
11163 |
ffi_set_ptr(&args[i], (void *) mjs_get_ptr(mjs, arg)); |
||
11164 |
} else if (mjs_is_null(arg)) { |
||
11165 |
ffi_set_ptr(&args[i], NULL); |
||
11166 |
} else { |
||
11167 |
ret = MJS_TYPE_ERROR; |
||
11168 |
mjs_prepend_errorf(mjs, ret, "actual arg #%d is not a ptr", i); |
||
11169 |
goto clean; |
||
11170 |
} |
||
11171 |
break; |
||
11172 |
case MJS_FFI_CTYPE_CALLBACK: |
||
11173 |
if (mjs_is_function(arg) || mjs_is_foreign(arg) || |
||
11174 |
mjs_is_ffi_sig(arg)) { |
||
11175 |
/* |
||
11176 |
* Current argument is a callback function pointer: remember the given |
||
11177 |
* JS function and the argument index |
||
11178 |
*/ |
||
11179 |
cbdata.func = arg; |
||
11180 |
cbdata.func_idx = i; |
||
11181 |
} else { |
||
11182 |
ret = MJS_TYPE_ERROR; |
||
11183 |
mjs_prepend_errorf(mjs, ret, |
||
11184 |
"actual arg #%d is not a function, but %s", i, |
||
11185 |
mjs_stringify_type((enum mjs_type) arg)); |
||
11186 |
goto clean; |
||
11187 |
} |
||
11188 |
break; |
||
11189 |
case MJS_FFI_CTYPE_INVALID: |
||
11190 |
/* parse_cval_type() has already set a more detailed error */ |
||
11191 |
ret = MJS_TYPE_ERROR; |
||
11192 |
mjs_prepend_errorf(mjs, ret, "wrong arg type"); |
||
11193 |
goto clean; |
||
11194 |
default: |
||
11195 |
abort(); |
||
11196 |
break; |
||
11197 |
} |
||
11198 |
} |
||
11199 |
|||
11200 |
if (cbdata.userdata_idx >= 0 && cbdata.func_idx >= 0) { |
||
11201 |
struct mjs_ffi_cb_args *cbargs = NULL; |
||
11202 |
struct mjs_ffi_cb_args **pitem = NULL; |
||
11203 |
|||
11204 |
/* the function takes a callback */ |
||
11205 |
|||
11206 |
/* |
||
11207 |
* Get cbargs: either reuse the existing one (if the matching item exists), |
||
11208 |
* or create a new one. |
||
11209 |
*/ |
||
11210 |
pitem = ffi_get_matching(&mjs->ffi_cb_args, cbdata.func, cbdata.userdata); |
||
11211 |
if (*pitem == NULL) { |
||
11212 |
/* No matching cbargs item; we need to add a new one */ |
||
11213 |
cbargs = calloc(1, sizeof(*cbargs)); |
||
11214 |
cbargs->mjs = mjs; |
||
11215 |
cbargs->func = cbdata.func; |
||
11216 |
cbargs->userdata = cbdata.userdata; |
||
11217 |
mjs_ffi_sig_copy(&cbargs->sig, psig->cb_sig); |
||
11218 |
|||
11219 |
/* Establish a link to the newly allocated item */ |
||
11220 |
*pitem = cbargs; |
||
11221 |
} else { |
||
11222 |
/* Found matching item: reuse it */ |
||
11223 |
cbargs = *pitem; |
||
11224 |
} |
||
11225 |
|||
11226 |
{ |
||
11227 |
union { |
||
11228 |
ffi_fn_t *fn; |
||
11229 |
void *p; |
||
11230 |
} u; |
||
11231 |
u.fn = psig->cb_sig->fn; |
||
11232 |
ffi_set_ptr(&args[cbdata.func_idx], u.p); |
||
11233 |
ffi_set_ptr(&args[cbdata.userdata_idx], cbargs); |
||
11234 |
} |
||
11235 |
} else if (!(cbdata.userdata_idx == -1 && cbdata.func_idx == -1)) { |
||
11236 |
/* |
||
11237 |
* incomplete signature: it contains either the function pointer or |
||
11238 |
* userdata. It should contain both or none. |
||
11239 |
* |
||
11240 |
* It should be handled in mjs_parse_ffi_signature(). |
||
11241 |
*/ |
||
11242 |
abort(); |
||
11243 |
} |
||
11244 |
|||
11245 |
ffi_call(psig->fn, nargs, &res, args); |
||
11246 |
|||
11247 |
switch (rtype) { |
||
11248 |
case MJS_FFI_CTYPE_CHAR_PTR: { |
||
11249 |
const char *s = (const char *) (uintptr_t) res.v.i; |
||
11250 |
if (s != NULL) { |
||
11251 |
resv = mjs_mk_string(mjs, s, ~0, 1); |
||
11252 |
} else { |
||
11253 |
resv = MJS_NULL; |
||
11254 |
} |
||
11255 |
break; |
||
11256 |
} |
||
11257 |
case MJS_FFI_CTYPE_VOID_PTR: |
||
11258 |
resv = mjs_mk_foreign(mjs, (void *) (uintptr_t) res.v.i); |
||
11259 |
break; |
||
11260 |
case MJS_FFI_CTYPE_INT: |
||
11261 |
resv = mjs_mk_number(mjs, (int) res.v.i); |
||
11262 |
break; |
||
11263 |
case MJS_FFI_CTYPE_BOOL: |
||
11264 |
resv = mjs_mk_boolean(mjs, !!res.v.i); |
||
11265 |
break; |
||
11266 |
case MJS_FFI_CTYPE_DOUBLE: |
||
11267 |
resv = mjs_mk_number(mjs, res.v.d); |
||
11268 |
break; |
||
11269 |
case MJS_FFI_CTYPE_FLOAT: |
||
11270 |
resv = mjs_mk_number(mjs, res.v.f); |
||
11271 |
break; |
||
11272 |
default: |
||
11273 |
resv = mjs_mk_undefined(); |
||
11274 |
break; |
||
11275 |
} |
||
11276 |
|||
11277 |
clean: |
||
11278 |
/* |
||
11279 |
* If there was some error, prepend an error message with the subject |
||
11280 |
* signature |
||
11281 |
*/ |
||
11282 |
if (ret != MJS_OK) { |
||
11283 |
mjs_prepend_errorf(mjs, ret, "failed to call FFIed function"); |
||
11284 |
/* TODO(dfrank) stringify mjs_ffi_sig_t in some human-readable format */ |
||
11285 |
} |
||
11286 |
mjs_return(mjs, resv); |
||
11287 |
|||
11288 |
return ret; |
||
11289 |
} |
||
11290 |
|||
11291 |
/* |
||
11292 |
* TODO(dfrank): make it return boolean (when booleans are supported), instead |
||
11293 |
* of a number |
||
11294 |
*/ |
||
11295 |
MJS_PRIVATE void mjs_ffi_cb_free(struct mjs *mjs) { |
||
11296 |
mjs_val_t ret = mjs_mk_number(mjs, 0); |
||
11297 |
mjs_val_t func = mjs_arg(mjs, 0); |
||
11298 |
mjs_val_t userdata = mjs_arg(mjs, 1); |
||
11299 |
|||
11300 |
if (mjs_is_function(func)) { |
||
11301 |
struct mjs_ffi_cb_args **pitem = |
||
11302 |
ffi_get_matching(&mjs->ffi_cb_args, func, userdata); |
||
11303 |
if (*pitem != NULL) { |
||
11304 |
/* Found matching item: remove it from the linked list, and free */ |
||
11305 |
struct mjs_ffi_cb_args *cbargs = *pitem; |
||
11306 |
*pitem = cbargs->next; |
||
11307 |
mjs_ffi_sig_free(&cbargs->sig); |
||
11308 |
free(cbargs); |
||
11309 |
ret = mjs_mk_number(mjs, 1); |
||
11310 |
} |
||
11311 |
} else { |
||
11312 |
mjs_prepend_errorf(mjs, MJS_TYPE_ERROR, "missing argument 'func'"); |
||
11313 |
} |
||
11314 |
|||
11315 |
mjs_return(mjs, ret); |
||
11316 |
} |
||
11317 |
|||
11318 |
78 |
void mjs_ffi_args_free_list(struct mjs *mjs) { |
|
11319 |
78 |
ffi_cb_args_t *next = mjs->ffi_cb_args; |
|
11320 |
|||
11321 |
✗✓ | 156 |
while (next != NULL) { |
11322 |
ffi_cb_args_t *cur = next; |
||
11323 |
next = next->next; |
||
11324 |
free(cur); |
||
11325 |
} |
||
11326 |
78 |
} |
|
11327 |
|||
11328 |
MJS_PRIVATE void mjs_ffi_sig_init(mjs_ffi_sig_t *sig) { |
||
11329 |
memset(sig, 0, sizeof(*sig)); |
||
11330 |
} |
||
11331 |
|||
11332 |
MJS_PRIVATE void mjs_ffi_sig_copy(mjs_ffi_sig_t *to, |
||
11333 |
const mjs_ffi_sig_t *from) { |
||
11334 |
memcpy(to, from, sizeof(*to)); |
||
11335 |
if (from->cb_sig != NULL) { |
||
11336 |
to->cb_sig = calloc(sizeof(*to->cb_sig), 1); |
||
11337 |
mjs_ffi_sig_copy(to->cb_sig, from->cb_sig); |
||
11338 |
} |
||
11339 |
} |
||
11340 |
|||
11341 |
MJS_PRIVATE void mjs_ffi_sig_free(mjs_ffi_sig_t *sig) { |
||
11342 |
if (sig->cb_sig != NULL) { |
||
11343 |
free(sig->cb_sig); |
||
11344 |
sig->cb_sig = NULL; |
||
11345 |
} |
||
11346 |
} |
||
11347 |
|||
11348 |
MJS_PRIVATE int mjs_ffi_sig_set_val_type(mjs_ffi_sig_t *sig, int idx, |
||
11349 |
mjs_ffi_ctype_t type) { |
||
11350 |
if (idx < MJS_CB_SIGNATURE_MAX_SIZE) { |
||
11351 |
sig->val_types[idx] = type; |
||
11352 |
return 1; |
||
11353 |
} else { |
||
11354 |
/* Index is too large */ |
||
11355 |
return 0; |
||
11356 |
} |
||
11357 |
} |
||
11358 |
|||
11359 |
MJS_PRIVATE int mjs_ffi_sig_validate(struct mjs *mjs, mjs_ffi_sig_t *sig, |
||
11360 |
enum ffi_sig_type sig_type) { |
||
11361 |
int ret = 0; |
||
11362 |
int i; |
||
11363 |
int callback_idx = 0; |
||
11364 |
int userdata_idx = 0; |
||
11365 |
|||
11366 |
sig->is_valid = 0; |
||
11367 |
|||
11368 |
switch (sig_type) { |
||
11369 |
case FFI_SIG_FUNC: |
||
11370 |
/* Make sure return type is fine */ |
||
11371 |
if (sig->val_types[0] != MJS_FFI_CTYPE_NONE && |
||
11372 |
sig->val_types[0] != MJS_FFI_CTYPE_INT && |
||
11373 |
sig->val_types[0] != MJS_FFI_CTYPE_BOOL && |
||
11374 |
sig->val_types[0] != MJS_FFI_CTYPE_DOUBLE && |
||
11375 |
sig->val_types[0] != MJS_FFI_CTYPE_FLOAT && |
||
11376 |
sig->val_types[0] != MJS_FFI_CTYPE_VOID_PTR && |
||
11377 |
sig->val_types[0] != MJS_FFI_CTYPE_CHAR_PTR) { |
||
11378 |
mjs_prepend_errorf(mjs, MJS_TYPE_ERROR, "invalid return value type"); |
||
11379 |
goto clean; |
||
11380 |
} |
||
11381 |
break; |
||
11382 |
case FFI_SIG_CALLBACK: |
||
11383 |
/* Make sure return type is fine */ |
||
11384 |
if (sig->val_types[0] != MJS_FFI_CTYPE_NONE && |
||
11385 |
sig->val_types[0] != MJS_FFI_CTYPE_INT && |
||
11386 |
sig->val_types[0] != MJS_FFI_CTYPE_BOOL && |
||
11387 |
sig->val_types[0] != MJS_FFI_CTYPE_DOUBLE && |
||
11388 |
sig->val_types[0] != MJS_FFI_CTYPE_FLOAT && |
||
11389 |
sig->val_types[0] != MJS_FFI_CTYPE_VOID_PTR) { |
||
11390 |
mjs_prepend_errorf(mjs, MJS_TYPE_ERROR, "invalid return value type"); |
||
11391 |
goto clean; |
||
11392 |
} |
||
11393 |
} |
||
11394 |
|||
11395 |
/* Handle argument types */ |
||
11396 |
for (i = 1; i < MJS_CB_SIGNATURE_MAX_SIZE; i++) { |
||
11397 |
mjs_ffi_ctype_t type = sig->val_types[i]; |
||
11398 |
switch (type) { |
||
11399 |
case MJS_FFI_CTYPE_USERDATA: |
||
11400 |
if (userdata_idx != 0) { |
||
11401 |
/* There must be at most one userdata arg, but we have more */ |
||
11402 |
mjs_prepend_errorf(mjs, MJS_TYPE_ERROR, |
||
11403 |
"more than one userdata arg: #%d and #%d", |
||
11404 |
(userdata_idx - 1), (i - 1)); |
||
11405 |
goto clean; |
||
11406 |
} |
||
11407 |
userdata_idx = i; |
||
11408 |
break; |
||
11409 |
case MJS_FFI_CTYPE_CALLBACK: |
||
11410 |
switch (sig_type) { |
||
11411 |
case FFI_SIG_FUNC: |
||
11412 |
break; |
||
11413 |
case FFI_SIG_CALLBACK: |
||
11414 |
mjs_prepend_errorf(mjs, MJS_TYPE_ERROR, |
||
11415 |
"callback can't take another callback"); |
||
11416 |
goto clean; |
||
11417 |
} |
||
11418 |
callback_idx = i; |
||
11419 |
break; |
||
11420 |
case MJS_FFI_CTYPE_INT: |
||
11421 |
case MJS_FFI_CTYPE_BOOL: |
||
11422 |
case MJS_FFI_CTYPE_VOID_PTR: |
||
11423 |
case MJS_FFI_CTYPE_CHAR_PTR: |
||
11424 |
case MJS_FFI_CTYPE_STRUCT_MG_STR_PTR: |
||
11425 |
case MJS_FFI_CTYPE_DOUBLE: |
||
11426 |
case MJS_FFI_CTYPE_FLOAT: |
||
11427 |
/* Do nothing */ |
||
11428 |
break; |
||
11429 |
case MJS_FFI_CTYPE_NONE: |
||
11430 |
/* No more arguments */ |
||
11431 |
goto args_over; |
||
11432 |
default: |
||
11433 |
mjs_prepend_errorf(mjs, MJS_INTERNAL_ERROR, "invalid ffi_ctype: %d", |
||
11434 |
type); |
||
11435 |
goto clean; |
||
11436 |
} |
||
11437 |
|||
11438 |
sig->args_cnt++; |
||
11439 |
} |
||
11440 |
args_over: |
||
11441 |
|||
11442 |
switch (sig_type) { |
||
11443 |
case FFI_SIG_FUNC: |
||
11444 |
if (!((callback_idx > 0 && userdata_idx > 0) || |
||
11445 |
(callback_idx == 0 && userdata_idx == 0))) { |
||
11446 |
mjs_prepend_errorf(mjs, MJS_TYPE_ERROR, |
||
11447 |
"callback and userdata should be either both " |
||
11448 |
"present or both absent"); |
||
11449 |
goto clean; |
||
11450 |
} |
||
11451 |
break; |
||
11452 |
case FFI_SIG_CALLBACK: |
||
11453 |
if (userdata_idx == 0) { |
||
11454 |
/* No userdata arg */ |
||
11455 |
mjs_prepend_errorf(mjs, MJS_TYPE_ERROR, "no userdata arg"); |
||
11456 |
goto clean; |
||
11457 |
} |
||
11458 |
break; |
||
11459 |
} |
||
11460 |
|||
11461 |
ret = 1; |
||
11462 |
|||
11463 |
clean: |
||
11464 |
if (ret) { |
||
11465 |
sig->is_valid = 1; |
||
11466 |
} |
||
11467 |
return ret; |
||
11468 |
} |
||
11469 |
|||
11470 |
MJS_PRIVATE int mjs_ffi_is_regular_word(mjs_ffi_ctype_t type) { |
||
11471 |
switch (type) { |
||
11472 |
case MJS_FFI_CTYPE_INT: |
||
11473 |
case MJS_FFI_CTYPE_BOOL: |
||
11474 |
return 1; |
||
11475 |
default: |
||
11476 |
return 0; |
||
11477 |
} |
||
11478 |
} |
||
11479 |
|||
11480 |
MJS_PRIVATE int mjs_ffi_is_regular_word_or_void(mjs_ffi_ctype_t type) { |
||
11481 |
return (type == MJS_FFI_CTYPE_NONE || mjs_ffi_is_regular_word(type)); |
||
11482 |
} |
||
11483 |
|||
11484 |
#ifdef _WIN32 |
||
11485 |
void *dlsym(void *handle, const char *name) { |
||
11486 |
static HANDLE msvcrt_dll; |
||
11487 |
void *sym = NULL; |
||
11488 |
if (msvcrt_dll == NULL) msvcrt_dll = GetModuleHandle("msvcrt.dll"); |
||
11489 |
if ((sym = GetProcAddress(GetModuleHandle(NULL), name)) == NULL) { |
||
11490 |
sym = GetProcAddress(msvcrt_dll, name); |
||
11491 |
} |
||
11492 |
return sym; |
||
11493 |
} |
||
11494 |
#elif !defined(__unix__) && !defined(__APPLE__) |
||
11495 |
void *dlsym(void *handle, const char *name) { |
||
11496 |
(void) handle; |
||
11497 |
(void) name; |
||
11498 |
return NULL; |
||
11499 |
} |
||
11500 |
#endif |
||
11501 |
#ifdef MJS_MODULE_LINES |
||
11502 |
#line 1 "mjs/src/mjs_gc.c" |
||
11503 |
#endif |
||
11504 |
/* |
||
11505 |
* Copyright (c) 2014 Cesanta Software Limited |
||
11506 |
* All rights reserved |
||
11507 |
*/ |
||
11508 |
|||
11509 |
#include <stdio.h> |
||
11510 |
|||
11511 |
/* Amalgamated: #include "common/cs_varint.h" */ |
||
11512 |
/* Amalgamated: #include "common/mbuf.h" */ |
||
11513 |
|||
11514 |
/* Amalgamated: #include "mjs/src/mjs_core.h" */ |
||
11515 |
/* Amalgamated: #include "mjs/src/mjs_ffi.h" */ |
||
11516 |
/* Amalgamated: #include "mjs/src/mjs_gc.h" */ |
||
11517 |
/* Amalgamated: #include "mjs/src/mjs_internal.h" */ |
||
11518 |
/* Amalgamated: #include "mjs/src/mjs_object.h" */ |
||
11519 |
/* Amalgamated: #include "mjs/src/mjs_primitive.h" */ |
||
11520 |
/* Amalgamated: #include "mjs/src/mjs_string.h" */ |
||
11521 |
|||
11522 |
/* |
||
11523 |
* Macros for marking reachable things: use bit 0. |
||
11524 |
*/ |
||
11525 |
#define MARK(p) (((struct gc_cell *) (p))->head.word |= 1) |
||
11526 |
#define UNMARK(p) (((struct gc_cell *) (p))->head.word &= ~1) |
||
11527 |
#define MARKED(p) (((struct gc_cell *) (p))->head.word & 1) |
||
11528 |
|||
11529 |
/* |
||
11530 |
* Similar to `MARK()` / `UNMARK()` / `MARKED()`, but `.._FREE` counterparts |
||
11531 |
* are intended to mark free cells (as opposed to used ones), so they use |
||
11532 |
* bit 1. |
||
11533 |
*/ |
||
11534 |
#define MARK_FREE(p) (((struct gc_cell *) (p))->head.word |= 2) |
||
11535 |
#define UNMARK_FREE(p) (((struct gc_cell *) (p))->head.word &= ~2) |
||
11536 |
#define MARKED_FREE(p) (((struct gc_cell *) (p))->head.word & 2) |
||
11537 |
|||
11538 |
/* |
||
11539 |
* When each arena has that or less free cells, GC will be scheduled |
||
11540 |
*/ |
||
11541 |
#define GC_ARENA_CELLS_RESERVE 2 |
||
11542 |
|||
11543 |
static struct gc_block *gc_new_block(struct gc_arena *a, size_t size); |
||
11544 |
static void gc_free_block(struct gc_block *b); |
||
11545 |
static void gc_mark_mbuf_pt(struct mjs *mjs, const struct mbuf *mbuf); |
||
11546 |
|||
11547 |
235 |
MJS_PRIVATE struct mjs_object *new_object(struct mjs *mjs) { |
|
11548 |
235 |
return (struct mjs_object *) gc_alloc_cell(mjs, &mjs->object_arena); |
|
11549 |
} |
||
11550 |
|||
11551 |
1405 |
MJS_PRIVATE struct mjs_property *new_property(struct mjs *mjs) { |
|
11552 |
1405 |
return (struct mjs_property *) gc_alloc_cell(mjs, &mjs->property_arena); |
|
11553 |
} |
||
11554 |
|||
11555 |
MJS_PRIVATE struct mjs_ffi_sig *new_ffi_sig(struct mjs *mjs) { |
||
11556 |
return (struct mjs_ffi_sig *) gc_alloc_cell(mjs, &mjs->ffi_sig_arena); |
||
11557 |
} |
||
11558 |
|||
11559 |
/* Initializes a new arena. */ |
||
11560 |
234 |
MJS_PRIVATE void gc_arena_init(struct gc_arena *a, size_t cell_size, |
|
11561 |
size_t initial_size, size_t size_increment) { |
||
11562 |
✗✓ | 234 |
assert(cell_size >= sizeof(uintptr_t)); |
11563 |
|||
11564 |
234 |
memset(a, 0, sizeof(*a)); |
|
11565 |
234 |
a->cell_size = cell_size; |
|
11566 |
234 |
a->size_increment = size_increment; |
|
11567 |
234 |
a->blocks = gc_new_block(a, initial_size); |
|
11568 |
234 |
} |
|
11569 |
|||
11570 |
234 |
MJS_PRIVATE void gc_arena_destroy(struct mjs *mjs, struct gc_arena *a) { |
|
11571 |
struct gc_block *b; |
||
11572 |
|||
11573 |
✓✗ | 234 |
if (a->blocks != NULL) { |
11574 |
234 |
gc_sweep(mjs, a, 0); |
|
11575 |
✓✓ | 702 |
for (b = a->blocks; b != NULL;) { |
11576 |
struct gc_block *tmp; |
||
11577 |
234 |
tmp = b; |
|
11578 |
234 |
b = b->next; |
|
11579 |
234 |
gc_free_block(tmp); |
|
11580 |
} |
||
11581 |
} |
||
11582 |
234 |
} |
|
11583 |
|||
11584 |
234 |
static void gc_free_block(struct gc_block *b) { |
|
11585 |
234 |
free(b->base); |
|
11586 |
234 |
free(b); |
|
11587 |
234 |
} |
|
11588 |
|||
11589 |
234 |
static struct gc_block *gc_new_block(struct gc_arena *a, size_t size) { |
|
11590 |
struct gc_cell *cur; |
||
11591 |
struct gc_block *b; |
||
11592 |
|||
11593 |
234 |
b = (struct gc_block *) calloc(1, sizeof(*b)); |
|
11594 |
✗✓ | 234 |
if (b == NULL) abort(); |
11595 |
|||
11596 |
234 |
b->size = size; |
|
11597 |
234 |
b->base = (struct gc_cell *) calloc(a->cell_size, b->size); |
|
11598 |
✗✓ | 234 |
if (b->base == NULL) abort(); |
11599 |
|||
11600 |
✓✓ | 5148 |
for (cur = GC_CELL_OP(a, b->base, +, 0); |
11601 |
4914 |
cur < GC_CELL_OP(a, b->base, +, b->size); |
|
11602 |
4680 |
cur = GC_CELL_OP(a, cur, +, 1)) { |
|
11603 |
4680 |
cur->head.link = a->free; |
|
11604 |
4680 |
a->free = cur; |
|
11605 |
} |
||
11606 |
|||
11607 |
234 |
return b; |
|
11608 |
} |
||
11609 |
|||
11610 |
/* |
||
11611 |
* Returns whether the given arena has GC_ARENA_CELLS_RESERVE or less free |
||
11612 |
* cells |
||
11613 |
*/ |
||
11614 |
1640 |
static int gc_arena_is_gc_needed(struct gc_arena *a) { |
|
11615 |
1640 |
struct gc_cell *r = a->free; |
|
11616 |
int i; |
||
11617 |
|||
11618 |
✓✓ | 6480 |
for (i = 0; i <= GC_ARENA_CELLS_RESERVE; i++, r = r->head.link) { |
11619 |
✓✓ | 4919 |
if (r == NULL) { |
11620 |
79 |
return 1; |
|
11621 |
} |
||
11622 |
} |
||
11623 |
|||
11624 |
1561 |
return 0; |
|
11625 |
} |
||
11626 |
|||
11627 |
472 |
MJS_PRIVATE int gc_strings_is_gc_needed(struct mjs *mjs) { |
|
11628 |
472 |
struct mbuf *m = &mjs->owned_strings; |
|
11629 |
472 |
return (double) m->len / (double) m->size > 0.9; |
|
11630 |
} |
||
11631 |
|||
11632 |
1640 |
MJS_PRIVATE void *gc_alloc_cell(struct mjs *mjs, struct gc_arena *a) { |
|
11633 |
struct gc_cell *r; |
||
11634 |
|||
11635 |
✗✓ | 1640 |
if (a->free == NULL) { |
11636 |
struct gc_block *b = gc_new_block(a, a->size_increment); |
||
11637 |
b->next = a->blocks; |
||
11638 |
a->blocks = b; |
||
11639 |
} |
||
11640 |
1640 |
r = a->free; |
|
11641 |
|||
11642 |
1640 |
UNMARK(r); |
|
11643 |
|||
11644 |
1640 |
a->free = r->head.link; |
|
11645 |
|||
11646 |
#if MJS_MEMORY_STATS |
||
11647 |
a->allocations++; |
||
11648 |
a->alive++; |
||
11649 |
#endif |
||
11650 |
|||
11651 |
/* Schedule GC if needed */ |
||
11652 |
✓✓ | 1640 |
if (gc_arena_is_gc_needed(a)) { |
11653 |
79 |
mjs->need_gc = 1; |
|
11654 |
} |
||
11655 |
|||
11656 |
/* |
||
11657 |
* TODO(mkm): minor opt possible since most of the fields |
||
11658 |
* are overwritten downstream, but not worth the yak shave time |
||
11659 |
* when fields are added to GC-able structures */ |
||
11660 |
1640 |
memset(r, 0, a->cell_size); |
|
11661 |
1640 |
return (void *) r; |
|
11662 |
} |
||
11663 |
|||
11664 |
/* |
||
11665 |
* Scans the arena and add all unmarked cells to the free list. |
||
11666 |
* |
||
11667 |
* Empty blocks get deallocated. The head of the free list will contais cells |
||
11668 |
* from the last (oldest) block. Cells will thus be allocated in block order. |
||
11669 |
*/ |
||
11670 |
435 |
void gc_sweep(struct mjs *mjs, struct gc_arena *a, size_t start) { |
|
11671 |
struct gc_block *b; |
||
11672 |
struct gc_cell *cur; |
||
11673 |
435 |
struct gc_block **prevp = &a->blocks; |
|
11674 |
#if MJS_MEMORY_STATS |
||
11675 |
a->alive = 0; |
||
11676 |
#endif |
||
11677 |
|||
11678 |
/* |
||
11679 |
* Before we sweep, we should mark all free cells in a way that is |
||
11680 |
* distinguishable from marked used cells. |
||
11681 |
*/ |
||
11682 |
{ |
||
11683 |
struct gc_cell *next; |
||
11684 |
✓✓ | 6087 |
for (cur = a->free; cur != NULL; cur = next) { |
11685 |
5652 |
next = cur->head.link; |
|
11686 |
5652 |
MARK_FREE(cur); |
|
11687 |
} |
||
11688 |
} |
||
11689 |
|||
11690 |
/* |
||
11691 |
* We'll rebuild the whole `free` list, so initially we just reset it |
||
11692 |
*/ |
||
11693 |
435 |
a->free = NULL; |
|
11694 |
|||
11695 |
✓✓ | 1305 |
for (b = a->blocks; b != NULL;) { |
11696 |
435 |
size_t freed_in_block = 0; |
|
11697 |
/* |
||
11698 |
* if it turns out that this block is 100% garbage |
||
11699 |
* we can release the whole block, but the addition |
||
11700 |
* of it's cells to the free list has to be undone. |
||
11701 |
*/ |
||
11702 |
435 |
struct gc_cell *prev_free = a->free; |
|
11703 |
|||
11704 |
✓✓ | 9570 |
for (cur = GC_CELL_OP(a, b->base, +, start); |
11705 |
9135 |
cur < GC_CELL_OP(a, b->base, +, b->size); |
|
11706 |
8700 |
cur = GC_CELL_OP(a, cur, +, 1)) { |
|
11707 |
✓✓ | 8700 |
if (MARKED(cur)) { |
11708 |
/* The cell is used and marked */ |
||
11709 |
1408 |
UNMARK(cur); |
|
11710 |
#if MJS_MEMORY_STATS |
||
11711 |
a->alive++; |
||
11712 |
#endif |
||
11713 |
} else { |
||
11714 |
/* |
||
11715 |
* The cell is either: |
||
11716 |
* - free |
||
11717 |
* - garbage that's about to be freed |
||
11718 |
*/ |
||
11719 |
|||
11720 |
✓✓ | 7292 |
if (MARKED_FREE(cur)) { |
11721 |
/* The cell is free, so, just unmark it */ |
||
11722 |
5652 |
UNMARK_FREE(cur); |
|
11723 |
} else { |
||
11724 |
/* |
||
11725 |
* The cell is used and should be freed: call the destructor and |
||
11726 |
* reset the memory |
||
11727 |
*/ |
||
11728 |
✗✓ | 1640 |
if (a->destructor != NULL) { |
11729 |
a->destructor(mjs, cur); |
||
11730 |
} |
||
11731 |
1640 |
memset(cur, 0, a->cell_size); |
|
11732 |
} |
||
11733 |
|||
11734 |
/* Add this cell to the `free` list */ |
||
11735 |
7292 |
cur->head.link = a->free; |
|
11736 |
7292 |
a->free = cur; |
|
11737 |
7292 |
freed_in_block++; |
|
11738 |
#if MJS_MEMORY_STATS |
||
11739 |
a->garbage++; |
||
11740 |
#endif |
||
11741 |
} |
||
11742 |
} |
||
11743 |
|||
11744 |
/* |
||
11745 |
* don't free the initial block, which is at the tail |
||
11746 |
* because it has a special size aimed at reducing waste |
||
11747 |
* and simplifying initial startup. TODO(mkm): improve |
||
11748 |
* */ |
||
11749 |
✗✓✗✗ |
435 |
if (b->next != NULL && freed_in_block == b->size) { |
11750 |
*prevp = b->next; |
||
11751 |
gc_free_block(b); |
||
11752 |
b = *prevp; |
||
11753 |
a->free = prev_free; |
||
11754 |
} else { |
||
11755 |
435 |
prevp = &b->next; |
|
11756 |
435 |
b = b->next; |
|
11757 |
} |
||
11758 |
} |
||
11759 |
435 |
} |
|
11760 |
|||
11761 |
/* Mark an FFI signature */ |
||
11762 |
static void gc_mark_ffi_sig(struct mjs *mjs, mjs_val_t *v) { |
||
11763 |
struct mjs_ffi_sig *psig; |
||
11764 |
|||
11765 |
assert(mjs_is_ffi_sig(*v)); |
||
11766 |
|||
11767 |
psig = mjs_get_ffi_sig_struct(*v); |
||
11768 |
|||
11769 |
/* |
||
11770 |
* we treat all object like things like objects but they might be functions, |
||
11771 |
* gc_check_val checks the appropriate arena per actual value type. |
||
11772 |
*/ |
||
11773 |
if (!gc_check_val(mjs, *v)) { |
||
11774 |
abort(); |
||
11775 |
} |
||
11776 |
|||
11777 |
if (MARKED(psig)) return; |
||
11778 |
|||
11779 |
MARK(psig); |
||
11780 |
} |
||
11781 |
|||
11782 |
/* Mark an object */ |
||
11783 |
268 |
static void gc_mark_object(struct mjs *mjs, mjs_val_t *v) { |
|
11784 |
struct mjs_object *obj_base; |
||
11785 |
struct mjs_property *prop; |
||
11786 |
struct mjs_property *next; |
||
11787 |
|||
11788 |
✗✓ | 268 |
assert(mjs_is_object(*v)); |
11789 |
|||
11790 |
268 |
obj_base = get_object_struct(*v); |
|
11791 |
|||
11792 |
/* |
||
11793 |
* we treat all object like things like objects but they might be functions, |
||
11794 |
* gc_check_val checks the appropriate arena per actual value type. |
||
11795 |
*/ |
||
11796 |
✗✓ | 268 |
if (!gc_check_val(mjs, *v)) { |
11797 |
abort(); |
||
11798 |
} |
||
11799 |
|||
11800 |
✓✓ | 268 |
if (MARKED(obj_base)) return; |
11801 |
|||
11802 |
/* mark object itself, and its properties */ |
||
11803 |
✓✓ | 1609 |
for ((prop = obj_base->properties), MARK(obj_base); prop != NULL; |
11804 |
1207 |
prop = next) { |
|
11805 |
✗✓ | 1207 |
if (!gc_check_ptr(&mjs->property_arena, prop)) { |
11806 |
abort(); |
||
11807 |
} |
||
11808 |
|||
11809 |
1207 |
gc_mark(mjs, &prop->name); |
|
11810 |
1207 |
gc_mark(mjs, &prop->value); |
|
11811 |
|||
11812 |
1207 |
next = prop->next; |
|
11813 |
1207 |
MARK(prop); |
|
11814 |
} |
||
11815 |
|||
11816 |
/* mark object's prototype */ |
||
11817 |
/* |
||
11818 |
* We dropped support for object prototypes in MJS. |
||
11819 |
* If we ever bring it back, don't forget to mark it |
||
11820 |
*/ |
||
11821 |
/* gc_mark(mjs, mjs_get_proto(mjs, v)); */ |
||
11822 |
} |
||
11823 |
|||
11824 |
/* Mark a string value */ |
||
11825 |
402 |
static void gc_mark_string(struct mjs *mjs, mjs_val_t *v) { |
|
11826 |
402 |
mjs_val_t h, tmp = 0; |
|
11827 |
char *s; |
||
11828 |
|||
11829 |
/* clang-format off */ |
||
11830 |
|||
11831 |
/* |
||
11832 |
* If a value points to an unmarked string we shall: |
||
11833 |
* 1. save the first 6 bytes of the string |
||
11834 |
* since we need to be able to distinguish real values from |
||
11835 |
* the saved first 6 bytes of the string, we need to tag the chunk |
||
11836 |
* as MJS_TAG_STRING_C |
||
11837 |
* 2. encode value's address (v) into the first 6 bytes of the string. |
||
11838 |
* 3. put the saved 8 bytes (tag + chunk) back into the value. |
||
11839 |
* 4. mark the string by putting '\1' in the NUL terminator of the previous |
||
11840 |
* string chunk. |
||
11841 |
* |
||
11842 |
* If a value points to an already marked string we shall: |
||
11843 |
* (0, <6 bytes of a pointer to a mjs_val_t>), hence we have to skip |
||
11844 |
* the first byte. We tag the value pointer as a MJS_TAG_FOREIGN |
||
11845 |
* so that it won't be followed during recursive mark. |
||
11846 |
* |
||
11847 |
* ... the rest is the same |
||
11848 |
* |
||
11849 |
* Note: 64-bit pointers can be represented with 48-bits |
||
11850 |
*/ |
||
11851 |
|||
11852 |
/* clang-format on */ |
||
11853 |
|||
11854 |
✗✓ | 402 |
assert((*v & MJS_TAG_MASK) == MJS_TAG_STRING_O); |
11855 |
|||
11856 |
402 |
s = mjs->owned_strings.buf + gc_string_mjs_val_to_offset(*v); |
|
11857 |
✗✓ | 402 |
assert(s < mjs->owned_strings.buf + mjs->owned_strings.len); |
11858 |
✓✗ | 402 |
if (s[-1] == '\0') { |
11859 |
402 |
memcpy(&tmp, s, sizeof(tmp) - 2); |
|
11860 |
402 |
tmp |= MJS_TAG_STRING_C; |
|
11861 |
} else { |
||
11862 |
memcpy(&tmp, s, sizeof(tmp) - 2); |
||
11863 |
tmp |= MJS_TAG_FOREIGN; |
||
11864 |
} |
||
11865 |
|||
11866 |
402 |
h = (mjs_val_t)(uintptr_t) v; |
|
11867 |
402 |
s[-1] = 1; |
|
11868 |
402 |
memcpy(s, &h, sizeof(h) - 2); |
|
11869 |
402 |
memcpy(v, &tmp, sizeof(tmp)); |
|
11870 |
402 |
} |
|
11871 |
|||
11872 |
2682 |
MJS_PRIVATE void gc_mark(struct mjs *mjs, mjs_val_t *v) { |
|
11873 |
✓✓ | 2682 |
if (mjs_is_object(*v)) { |
11874 |
268 |
gc_mark_object(mjs, v); |
|
11875 |
} |
||
11876 |
✗✓ | 2682 |
if (mjs_is_ffi_sig(*v)) { |
11877 |
gc_mark_ffi_sig(mjs, v); |
||
11878 |
} |
||
11879 |
✓✓ | 2682 |
if ((*v & MJS_TAG_MASK) == MJS_TAG_STRING_O) { |
11880 |
402 |
gc_mark_string(mjs, v); |
|
11881 |
} |
||
11882 |
2682 |
} |
|
11883 |
|||
11884 |
3151 |
MJS_PRIVATE uint64_t gc_string_mjs_val_to_offset(mjs_val_t v) { |
|
11885 |
3151 |
return (((uint64_t)(uintptr_t) get_ptr(v)) & ~MJS_TAG_MASK); |
|
11886 |
} |
||
11887 |
|||
11888 |
402 |
MJS_PRIVATE mjs_val_t gc_string_val_from_offset(uint64_t s) { |
|
11889 |
402 |
return s | MJS_TAG_STRING_O; |
|
11890 |
} |
||
11891 |
|||
11892 |
67 |
void gc_compact_strings(struct mjs *mjs) { |
|
11893 |
67 |
char *p = mjs->owned_strings.buf + 1; |
|
11894 |
67 |
uint64_t h, next, head = 1; |
|
11895 |
int len, llen; |
||
11896 |
|||
11897 |
✓✓ | 536 |
while (p < mjs->owned_strings.buf + mjs->owned_strings.len) { |
11898 |
✓✗ | 402 |
if (p[-1] == '\1') { |
11899 |
/* relocate and update ptrs */ |
||
11900 |
402 |
h = 0; |
|
11901 |
402 |
memcpy(&h, p, sizeof(h) - 2); |
|
11902 |
|||
11903 |
/* |
||
11904 |
* relocate pointers until we find the tail. |
||
11905 |
* The tail is marked with MJS_TAG_STRING_C, |
||
11906 |
* while mjs_val_t link pointers are tagged with MJS_TAG_FOREIGN |
||
11907 |
*/ |
||
11908 |
✓✓ | 804 |
for (; (h & MJS_TAG_MASK) != MJS_TAG_STRING_C; h = next) { |
11909 |
402 |
h &= ~MJS_TAG_MASK; |
|
11910 |
402 |
memcpy(&next, (char *) (uintptr_t) h, sizeof(h)); |
|
11911 |
|||
11912 |
402 |
*(mjs_val_t *) (uintptr_t) h = gc_string_val_from_offset(head); |
|
11913 |
} |
||
11914 |
402 |
h &= ~MJS_TAG_MASK; |
|
11915 |
|||
11916 |
/* |
||
11917 |
* the tail contains the first 6 bytes we stole from |
||
11918 |
* the actual string. |
||
11919 |
*/ |
||
11920 |
402 |
len = cs_varint_decode_unsafe((unsigned char *) &h, &llen); |
|
11921 |
402 |
len += llen + 1; |
|
11922 |
|||
11923 |
/* |
||
11924 |
* restore the saved 6 bytes |
||
11925 |
* TODO(mkm): think about endianness |
||
11926 |
*/ |
||
11927 |
402 |
memcpy(p, &h, sizeof(h) - 2); |
|
11928 |
|||
11929 |
/* |
||
11930 |
* and relocate the string data by packing it to the left. |
||
11931 |
*/ |
||
11932 |
402 |
memmove(mjs->owned_strings.buf + head, p, len); |
|
11933 |
402 |
mjs->owned_strings.buf[head - 1] = 0x0; |
|
11934 |
402 |
p += len; |
|
11935 |
402 |
head += len; |
|
11936 |
} else { |
||
11937 |
len = cs_varint_decode_unsafe((unsigned char *) p, &llen); |
||
11938 |
len += llen + 1; |
||
11939 |
|||
11940 |
p += len; |
||
11941 |
} |
||
11942 |
} |
||
11943 |
|||
11944 |
67 |
mjs->owned_strings.len = head; |
|
11945 |
67 |
} |
|
11946 |
|||
11947 |
67 |
MJS_PRIVATE int maybe_gc(struct mjs *mjs) { |
|
11948 |
✓✗ | 67 |
if (!mjs->inhibit_gc) { |
11949 |
67 |
mjs_gc(mjs, 0); |
|
11950 |
67 |
return 1; |
|
11951 |
} |
||
11952 |
return 0; |
||
11953 |
} |
||
11954 |
|||
11955 |
/* |
||
11956 |
* mark an array of `mjs_val_t` values (*not pointers* to them) |
||
11957 |
*/ |
||
11958 |
268 |
static void gc_mark_val_array(struct mjs *mjs, mjs_val_t *vals, size_t len) { |
|
11959 |
mjs_val_t *vp; |
||
11960 |
✓✓ | 536 |
for (vp = vals; vp < vals + len; vp++) { |
11961 |
268 |
gc_mark(mjs, vp); |
|
11962 |
} |
||
11963 |
268 |
} |
|
11964 |
|||
11965 |
/* |
||
11966 |
* mark an mbuf containing *pointers* to `mjs_val_t` values |
||
11967 |
*/ |
||
11968 |
67 |
static void gc_mark_mbuf_pt(struct mjs *mjs, const struct mbuf *mbuf) { |
|
11969 |
mjs_val_t **vp; |
||
11970 |
✗✓ | 134 |
for (vp = (mjs_val_t **) mbuf->buf; (char *) vp < mbuf->buf + mbuf->len; |
11971 |
vp++) { |
||
11972 |
gc_mark(mjs, *vp); |
||
11973 |
} |
||
11974 |
67 |
} |
|
11975 |
|||
11976 |
/* |
||
11977 |
* mark an mbuf containing `mjs_val_t` values (*not pointers* to them) |
||
11978 |
*/ |
||
11979 |
201 |
static void gc_mark_mbuf_val(struct mjs *mjs, const struct mbuf *mbuf) { |
|
11980 |
201 |
gc_mark_val_array(mjs, (mjs_val_t *) mbuf->buf, |
|
11981 |
201 |
mbuf->len / sizeof(mjs_val_t)); |
|
11982 |
201 |
} |
|
11983 |
|||
11984 |
67 |
static void gc_mark_ffi_cbargs_list(struct mjs *mjs, ffi_cb_args_t *cbargs) { |
|
11985 |
✗✓ | 67 |
for (; cbargs != NULL; cbargs = cbargs->next) { |
11986 |
gc_mark(mjs, &cbargs->func); |
||
11987 |
gc_mark(mjs, &cbargs->userdata); |
||
11988 |
} |
||
11989 |
67 |
} |
|
11990 |
|||
11991 |
/* Perform garbage collection */ |
||
11992 |
67 |
void mjs_gc(struct mjs *mjs, int full) { |
|
11993 |
67 |
gc_mark_val_array(mjs, (mjs_val_t *) &mjs->vals, |
|
11994 |
sizeof(mjs->vals) / sizeof(mjs_val_t)); |
||
11995 |
|||
11996 |
67 |
gc_mark_mbuf_pt(mjs, &mjs->owned_values); |
|
11997 |
67 |
gc_mark_mbuf_val(mjs, &mjs->scopes); |
|
11998 |
67 |
gc_mark_mbuf_val(mjs, &mjs->stack); |
|
11999 |
67 |
gc_mark_mbuf_val(mjs, &mjs->call_stack); |
|
12000 |
|||
12001 |
67 |
gc_mark_ffi_cbargs_list(mjs, mjs->ffi_cb_args); |
|
12002 |
|||
12003 |
67 |
gc_compact_strings(mjs); |
|
12004 |
|||
12005 |
67 |
gc_sweep(mjs, &mjs->object_arena, 0); |
|
12006 |
67 |
gc_sweep(mjs, &mjs->property_arena, 0); |
|
12007 |
67 |
gc_sweep(mjs, &mjs->ffi_sig_arena, 0); |
|
12008 |
|||
12009 |
✗✓ | 67 |
if (full) { |
12010 |
/* |
||
12011 |
* In case of full GC, we also resize strings buffer, but we still leave |
||
12012 |
* some extra space (at most, `_MJS_STRING_BUF_RESERVE`) in order to avoid |
||
12013 |
* frequent reallocations |
||
12014 |
*/ |
||
12015 |
size_t trimmed_size = mjs->owned_strings.len + _MJS_STRING_BUF_RESERVE; |
||
12016 |
if (trimmed_size < mjs->owned_strings.size) { |
||
12017 |
mbuf_resize(&mjs->owned_strings, trimmed_size); |
||
12018 |
} |
||
12019 |
} |
||
12020 |
67 |
} |
|
12021 |
|||
12022 |
268 |
MJS_PRIVATE int gc_check_val(struct mjs *mjs, mjs_val_t v) { |
|
12023 |
✓✗ | 268 |
if (mjs_is_object(v)) { |
12024 |
268 |
return gc_check_ptr(&mjs->object_arena, get_object_struct(v)); |
|
12025 |
} |
||
12026 |
if (mjs_is_ffi_sig(v)) { |
||
12027 |
return gc_check_ptr(&mjs->ffi_sig_arena, mjs_get_ffi_sig_struct(v)); |
||
12028 |
} |
||
12029 |
return 1; |
||
12030 |
} |
||
12031 |
|||
12032 |
1475 |
MJS_PRIVATE int gc_check_ptr(const struct gc_arena *a, const void *ptr) { |
|
12033 |
1475 |
const struct gc_cell *p = (const struct gc_cell *) ptr; |
|
12034 |
struct gc_block *b; |
||
12035 |
✓✗ | 1475 |
for (b = a->blocks; b != NULL; b = b->next) { |
12036 |
✓✗✓✗ |
1475 |
if (p >= b->base && p < GC_CELL_OP(a, b->base, +, b->size)) { |
12037 |
1475 |
return 1; |
|
12038 |
} |
||
12039 |
} |
||
12040 |
return 0; |
||
12041 |
} |
||
12042 |
#ifdef MJS_MODULE_LINES |
||
12043 |
#line 1 "mjs/src/mjs_json.c" |
||
12044 |
#endif |
||
12045 |
/* |
||
12046 |
* Copyright (c) 2016 Cesanta Software Limited |
||
12047 |
* All rights reserved |
||
12048 |
*/ |
||
12049 |
|||
12050 |
/* Amalgamated: #include "common/str_util.h" */ |
||
12051 |
/* Amalgamated: #include "frozen.h" */ |
||
12052 |
/* Amalgamated: #include "mjs/src/mjs_array.h" */ |
||
12053 |
/* Amalgamated: #include "mjs/src/mjs_internal.h" */ |
||
12054 |
/* Amalgamated: #include "mjs/src/mjs_conversion.h" */ |
||
12055 |
/* Amalgamated: #include "mjs/src/mjs_core.h" */ |
||
12056 |
/* Amalgamated: #include "mjs/src/mjs_object.h" */ |
||
12057 |
/* Amalgamated: #include "mjs/src/mjs_primitive.h" */ |
||
12058 |
/* Amalgamated: #include "mjs/src/mjs_string.h" */ |
||
12059 |
|||
12060 |
#define BUF_LEFT(size, used) (((size_t)(used) < (size)) ? ((size) - (used)) : 0) |
||
12061 |
|||
12062 |
/* |
||
12063 |
* Returns whether the value of given type should be skipped when generating |
||
12064 |
* JSON output |
||
12065 |
* |
||
12066 |
* So far it always returns 0, but we might add some logic later, if we |
||
12067 |
* implement some non-jsonnable objects |
||
12068 |
*/ |
||
12069 |
static int should_skip_for_json(enum mjs_type type) { |
||
12070 |
int ret; |
||
12071 |
switch (type) { |
||
12072 |
/* All permitted values */ |
||
12073 |
case MJS_TYPE_NULL: |
||
12074 |
case MJS_TYPE_BOOLEAN: |
||
12075 |
case MJS_TYPE_NUMBER: |
||
12076 |
case MJS_TYPE_STRING: |
||
12077 |
case MJS_TYPE_OBJECT_GENERIC: |
||
12078 |
case MJS_TYPE_OBJECT_ARRAY: |
||
12079 |
ret = 0; |
||
12080 |
break; |
||
12081 |
default: |
||
12082 |
ret = 1; |
||
12083 |
break; |
||
12084 |
} |
||
12085 |
return ret; |
||
12086 |
} |
||
12087 |
|||
12088 |
static const char *hex_digits = "0123456789abcdef"; |
||
12089 |
static char *append_hex(char *buf, char *limit, uint8_t c) { |
||
12090 |
if (buf < limit) *buf++ = 'u'; |
||
12091 |
if (buf < limit) *buf++ = '0'; |
||
12092 |
if (buf < limit) *buf++ = '0'; |
||
12093 |
if (buf < limit) *buf++ = hex_digits[(int) ((c >> 4) % 0xf)]; |
||
12094 |
if (buf < limit) *buf++ = hex_digits[(int) (c & 0xf)]; |
||
12095 |
return buf; |
||
12096 |
} |
||
12097 |
|||
12098 |
/* |
||
12099 |
* Appends quoted s to buf. Any double quote contained in s will be escaped. |
||
12100 |
* Returns the number of characters that would have been added, |
||
12101 |
* like snprintf. |
||
12102 |
* If size is zero it doesn't output anything but keeps counting. |
||
12103 |
*/ |
||
12104 |
static int snquote(char *buf, size_t size, const char *s, size_t len) { |
||
12105 |
char *limit = buf + size; |
||
12106 |
const char *end; |
||
12107 |
/* |
||
12108 |
* String single character escape sequence: |
||
12109 |
* http://www.ecma-international.org/ecma-262/6.0/index.html#table-34 |
||
12110 |
* |
||
12111 |
* 0x8 -> \b |
||
12112 |
* 0x9 -> \t |
||
12113 |
* 0xa -> \n |
||
12114 |
* 0xb -> \v |
||
12115 |
* 0xc -> \f |
||
12116 |
* 0xd -> \r |
||
12117 |
*/ |
||
12118 |
const char *specials = "btnvfr"; |
||
12119 |
size_t i = 0; |
||
12120 |
|||
12121 |
i++; |
||
12122 |
if (buf < limit) *buf++ = '"'; |
||
12123 |
|||
12124 |
for (end = s + len; s < end; s++) { |
||
12125 |
if (*s == '"' || *s == '\\') { |
||
12126 |
i++; |
||
12127 |
if (buf < limit) *buf++ = '\\'; |
||
12128 |
} else if (*s >= '\b' && *s <= '\r') { |
||
12129 |
i += 2; |
||
12130 |
if (buf < limit) *buf++ = '\\'; |
||
12131 |
if (buf < limit) *buf++ = specials[*s - '\b']; |
||
12132 |
continue; |
||
12133 |
} else if ((unsigned char) *s < '\b' || (*s > '\r' && *s < ' ')) { |
||
12134 |
i += 6 /* \uXXXX */; |
||
12135 |
if (buf < limit) *buf++ = '\\'; |
||
12136 |
buf = append_hex(buf, limit, (uint8_t) *s); |
||
12137 |
continue; |
||
12138 |
} |
||
12139 |
i++; |
||
12140 |
if (buf < limit) *buf++ = *s; |
||
12141 |
} |
||
12142 |
|||
12143 |
i++; |
||
12144 |
if (buf < limit) *buf++ = '"'; |
||
12145 |
|||
12146 |
if (buf < limit) { |
||
12147 |
*buf = '\0'; |
||
12148 |
} else if (size != 0) { |
||
12149 |
/* |
||
12150 |
* There is no room for the NULL char, but the size wasn't zero, so we can |
||
12151 |
* safely put NULL in the previous byte |
||
12152 |
*/ |
||
12153 |
*(buf - 1) = '\0'; |
||
12154 |
} |
||
12155 |
return i; |
||
12156 |
} |
||
12157 |
|||
12158 |
MJS_PRIVATE mjs_err_t to_json_or_debug(struct mjs *mjs, mjs_val_t v, char *buf, |
||
12159 |
size_t size, size_t *res_len, |
||
12160 |
uint8_t is_debug) { |
||
12161 |
mjs_val_t el; |
||
12162 |
char *vp; |
||
12163 |
mjs_err_t rcode = MJS_OK; |
||
12164 |
size_t len = 0; |
||
12165 |
/* |
||
12166 |
* TODO(dfrank) : also push all `mjs_val_t`s that are declared below |
||
12167 |
*/ |
||
12168 |
|||
12169 |
if (size > 0) *buf = '\0'; |
||
12170 |
|||
12171 |
if (!is_debug && should_skip_for_json(mjs_get_type(v))) { |
||
12172 |
goto clean; |
||
12173 |
} |
||
12174 |
|||
12175 |
for (vp = mjs->json_visited_stack.buf; |
||
12176 |
vp < mjs->json_visited_stack.buf + mjs->json_visited_stack.len; |
||
12177 |
vp += sizeof(mjs_val_t)) { |
||
12178 |
if (*(mjs_val_t *) vp == v) { |
||
12179 |
strncpy(buf, "[Circular]", size); |
||
12180 |
len = 10; |
||
12181 |
goto clean; |
||
12182 |
} |
||
12183 |
} |
||
12184 |
|||
12185 |
switch (mjs_get_type(v)) { |
||
12186 |
case MJS_TYPE_NULL: |
||
12187 |
case MJS_TYPE_BOOLEAN: |
||
12188 |
case MJS_TYPE_NUMBER: |
||
12189 |
case MJS_TYPE_UNDEFINED: |
||
12190 |
case MJS_TYPE_FOREIGN: |
||
12191 |
/* For those types, regular `mjs_to_string()` works */ |
||
12192 |
{ |
||
12193 |
/* TODO: refactor: mjs_to_string allocates memory every time */ |
||
12194 |
char *p = NULL; |
||
12195 |
int need_free = 0; |
||
12196 |
rcode = mjs_to_string(mjs, &v, &p, &len, &need_free); |
||
12197 |
c_snprintf(buf, size, "%.*s", (int) len, p); |
||
12198 |
if (need_free) { |
||
12199 |
free(p); |
||
12200 |
} |
||
12201 |
} |
||
12202 |
goto clean; |
||
12203 |
|||
12204 |
case MJS_TYPE_STRING: { |
||
12205 |
/* |
||
12206 |
* For strings we can't just use `primitive_to_str()`, because we need |
||
12207 |
* quoted value |
||
12208 |
*/ |
||
12209 |
size_t n; |
||
12210 |
const char *str = mjs_get_string(mjs, &v, &n); |
||
12211 |
len = snquote(buf, size, str, n); |
||
12212 |
goto clean; |
||
12213 |
} |
||
12214 |
|||
12215 |
case MJS_TYPE_OBJECT_FUNCTION: |
||
12216 |
case MJS_TYPE_OBJECT_GENERIC: { |
||
12217 |
char *b = buf; |
||
12218 |
struct mjs_property *prop = NULL; |
||
12219 |
struct mjs_object *o = NULL; |
||
12220 |
|||
12221 |
mbuf_append(&mjs->json_visited_stack, (char *) &v, sizeof(v)); |
||
12222 |
b += c_snprintf(b, BUF_LEFT(size, b - buf), "{"); |
||
12223 |
o = get_object_struct(v); |
||
12224 |
for (prop = o->properties; prop != NULL; prop = prop->next) { |
||
12225 |
size_t n; |
||
12226 |
const char *s; |
||
12227 |
if (!is_debug && should_skip_for_json(mjs_get_type(prop->value))) { |
||
12228 |
continue; |
||
12229 |
} |
||
12230 |
if (b - buf != 1) { /* Not the first property to be printed */ |
||
12231 |
b += c_snprintf(b, BUF_LEFT(size, b - buf), ","); |
||
12232 |
} |
||
12233 |
s = mjs_get_string(mjs, &prop->name, &n); |
||
12234 |
b += c_snprintf(b, BUF_LEFT(size, b - buf), "\"%.*s\":", (int) n, s); |
||
12235 |
{ |
||
12236 |
size_t tmp = 0; |
||
12237 |
rcode = to_json_or_debug(mjs, prop->value, b, BUF_LEFT(size, b - buf), |
||
12238 |
&tmp, is_debug); |
||
12239 |
if (rcode != MJS_OK) { |
||
12240 |
goto clean_iter; |
||
12241 |
} |
||
12242 |
b += tmp; |
||
12243 |
} |
||
12244 |
} |
||
12245 |
|||
12246 |
b += c_snprintf(b, BUF_LEFT(size, b - buf), "}"); |
||
12247 |
mjs->json_visited_stack.len -= sizeof(v); |
||
12248 |
|||
12249 |
clean_iter: |
||
12250 |
len = b - buf; |
||
12251 |
goto clean; |
||
12252 |
} |
||
12253 |
case MJS_TYPE_OBJECT_ARRAY: { |
||
12254 |
int has; |
||
12255 |
char *b = buf; |
||
12256 |
size_t i, alen = mjs_array_length(mjs, v); |
||
12257 |
mbuf_append(&mjs->json_visited_stack, (char *) &v, sizeof(v)); |
||
12258 |
b += c_snprintf(b, BUF_LEFT(size, b - buf), "["); |
||
12259 |
for (i = 0; i < alen; i++) { |
||
12260 |
el = mjs_array_get2(mjs, v, i, &has); |
||
12261 |
if (has) { |
||
12262 |
size_t tmp = 0; |
||
12263 |
if (!is_debug && should_skip_for_json(mjs_get_type(el))) { |
||
12264 |
b += c_snprintf(b, BUF_LEFT(size, b - buf), "null"); |
||
12265 |
} else { |
||
12266 |
rcode = to_json_or_debug(mjs, el, b, BUF_LEFT(size, b - buf), &tmp, |
||
12267 |
is_debug); |
||
12268 |
if (rcode != MJS_OK) { |
||
12269 |
goto clean; |
||
12270 |
} |
||
12271 |
} |
||
12272 |
b += tmp; |
||
12273 |
} else { |
||
12274 |
b += c_snprintf(b, BUF_LEFT(size, b - buf), "null"); |
||
12275 |
} |
||
12276 |
if (i != alen - 1) { |
||
12277 |
b += c_snprintf(b, BUF_LEFT(size, b - buf), ","); |
||
12278 |
} |
||
12279 |
} |
||
12280 |
b += c_snprintf(b, BUF_LEFT(size, b - buf), "]"); |
||
12281 |
mjs->json_visited_stack.len -= sizeof(v); |
||
12282 |
len = b - buf; |
||
12283 |
goto clean; |
||
12284 |
} |
||
12285 |
|||
12286 |
case MJS_TYPES_CNT: |
||
12287 |
abort(); |
||
12288 |
} |
||
12289 |
|||
12290 |
abort(); |
||
12291 |
|||
12292 |
len = 0; /* for compilers that don't know about abort() */ |
||
12293 |
goto clean; |
||
12294 |
|||
12295 |
clean: |
||
12296 |
if (rcode != MJS_OK) { |
||
12297 |
len = 0; |
||
12298 |
} |
||
12299 |
if (res_len != NULL) { |
||
12300 |
*res_len = len; |
||
12301 |
} |
||
12302 |
return rcode; |
||
12303 |
} |
||
12304 |
|||
12305 |
MJS_PRIVATE mjs_err_t mjs_json_stringify(struct mjs *mjs, mjs_val_t v, |
||
12306 |
char *buf, size_t size, char **res) { |
||
12307 |
mjs_err_t rcode = MJS_OK; |
||
12308 |
char *p = buf; |
||
12309 |
size_t len; |
||
12310 |
|||
12311 |
to_json_or_debug(mjs, v, buf, size, &len, 0); |
||
12312 |
|||
12313 |
if (len >= size) { |
||
12314 |
/* Buffer is not large enough. Allocate a bigger one */ |
||
12315 |
p = (char *) malloc(len + 1); |
||
12316 |
rcode = mjs_json_stringify(mjs, v, p, len + 1, res); |
||
12317 |
assert(*res == p); |
||
12318 |
goto clean; |
||
12319 |
} else { |
||
12320 |
*res = p; |
||
12321 |
goto clean; |
||
12322 |
} |
||
12323 |
|||
12324 |
clean: |
||
12325 |
/* |
||
12326 |
* If we're going to return an error, and we allocated a buffer, then free |
||
12327 |
* it. Otherwise, caller should free it. |
||
12328 |
*/ |
||
12329 |
if (rcode != MJS_OK && p != buf) { |
||
12330 |
free(p); |
||
12331 |
} |
||
12332 |
return rcode; |
||
12333 |
} |
||
12334 |
|||
12335 |
/* |
||
12336 |
* JSON parsing frame: a separate frame is allocated for each nested |
||
12337 |
* object/array during parsing |
||
12338 |
*/ |
||
12339 |
struct json_parse_frame { |
||
12340 |
mjs_val_t val; |
||
12341 |
struct json_parse_frame *up; |
||
12342 |
}; |
||
12343 |
|||
12344 |
/* |
||
12345 |
* Context for JSON parsing by means of json_walk() |
||
12346 |
*/ |
||
12347 |
struct json_parse_ctx { |
||
12348 |
struct mjs *mjs; |
||
12349 |
mjs_val_t result; |
||
12350 |
struct json_parse_frame *frame; |
||
12351 |
enum mjs_err rcode; |
||
12352 |
}; |
||
12353 |
|||
12354 |
/* Allocate JSON parse frame */ |
||
12355 |
static struct json_parse_frame *alloc_json_frame(struct json_parse_ctx *ctx, |
||
12356 |
mjs_val_t v) { |
||
12357 |
struct json_parse_frame *frame = |
||
12358 |
(struct json_parse_frame *) calloc(sizeof(struct json_parse_frame), 1); |
||
12359 |
frame->val = v; |
||
12360 |
mjs_own(ctx->mjs, &frame->val); |
||
12361 |
return frame; |
||
12362 |
} |
||
12363 |
|||
12364 |
/* Free JSON parse frame, return the previous one (which may be NULL) */ |
||
12365 |
static struct json_parse_frame *free_json_frame( |
||
12366 |
struct json_parse_ctx *ctx, struct json_parse_frame *frame) { |
||
12367 |
struct json_parse_frame *up = frame->up; |
||
12368 |
mjs_disown(ctx->mjs, &frame->val); |
||
12369 |
free(frame); |
||
12370 |
return up; |
||
12371 |
} |
||
12372 |
|||
12373 |
/* Callback for json_walk() */ |
||
12374 |
static void frozen_cb(void *data, const char *name, size_t name_len, |
||
12375 |
const char *path, const struct json_token *token) { |
||
12376 |
struct json_parse_ctx *ctx = (struct json_parse_ctx *) data; |
||
12377 |
mjs_val_t v = MJS_UNDEFINED; |
||
12378 |
|||
12379 |
(void) path; |
||
12380 |
|||
12381 |
mjs_own(ctx->mjs, &v); |
||
12382 |
|||
12383 |
switch (token->type) { |
||
12384 |
case JSON_TYPE_STRING: { |
||
12385 |
char *dst; |
||
12386 |
if (token->len > 0 && (dst = malloc(token->len)) != NULL) { |
||
12387 |
int len = json_unescape(token->ptr, token->len, dst, token->len); |
||
12388 |
if (len < 0) { |
||
12389 |
mjs_prepend_errorf(ctx->mjs, MJS_TYPE_ERROR, "invalid JSON string"); |
||
12390 |
break; |
||
12391 |
} |
||
12392 |
v = mjs_mk_string(ctx->mjs, dst, len, 1 /* copy */); |
||
12393 |
free(dst); |
||
12394 |
} else { |
||
12395 |
/* |
||
12396 |
* This branch is for 0-len strings, and for malloc errors |
||
12397 |
* TODO(lsm): on malloc error, propagate the error upstream |
||
12398 |
*/ |
||
12399 |
v = mjs_mk_string(ctx->mjs, "", 0, 1 /* copy */); |
||
12400 |
} |
||
12401 |
break; |
||
12402 |
} |
||
12403 |
case JSON_TYPE_NUMBER: |
||
12404 |
v = mjs_mk_number(ctx->mjs, strtod(token->ptr, NULL)); |
||
12405 |
break; |
||
12406 |
case JSON_TYPE_TRUE: |
||
12407 |
v = mjs_mk_boolean(ctx->mjs, 1); |
||
12408 |
break; |
||
12409 |
case JSON_TYPE_FALSE: |
||
12410 |
v = mjs_mk_boolean(ctx->mjs, 0); |
||
12411 |
break; |
||
12412 |
case JSON_TYPE_NULL: |
||
12413 |
v = MJS_NULL; |
||
12414 |
break; |
||
12415 |
case JSON_TYPE_OBJECT_START: |
||
12416 |
v = mjs_mk_object(ctx->mjs); |
||
12417 |
break; |
||
12418 |
case JSON_TYPE_ARRAY_START: |
||
12419 |
v = mjs_mk_array(ctx->mjs); |
||
12420 |
break; |
||
12421 |
|||
12422 |
case JSON_TYPE_OBJECT_END: |
||
12423 |
case JSON_TYPE_ARRAY_END: { |
||
12424 |
/* Object or array has finished: deallocate its frame */ |
||
12425 |
ctx->frame = free_json_frame(ctx, ctx->frame); |
||
12426 |
} break; |
||
12427 |
|||
12428 |
default: |
||
12429 |
LOG(LL_ERROR, ("Wrong token type %d\n", token->type)); |
||
12430 |
break; |
||
12431 |
} |
||
12432 |
|||
12433 |
if (!mjs_is_undefined(v)) { |
||
12434 |
if (name != NULL && name_len != 0) { |
||
12435 |
/* Need to define a property on the current object/array */ |
||
12436 |
if (mjs_is_object(ctx->frame->val)) { |
||
12437 |
mjs_set(ctx->mjs, ctx->frame->val, name, name_len, v); |
||
12438 |
} else if (mjs_is_array(ctx->frame->val)) { |
||
12439 |
/* |
||
12440 |
* TODO(dfrank): consult name_len. Currently it's not a problem due to |
||
12441 |
* the implementation details of frozen, but it might change |
||
12442 |
*/ |
||
12443 |
int idx = (int) strtod(name, NULL); |
||
12444 |
mjs_array_set(ctx->mjs, ctx->frame->val, idx, v); |
||
12445 |
} else { |
||
12446 |
LOG(LL_ERROR, ("Current value is neither object nor array\n")); |
||
12447 |
} |
||
12448 |
} else { |
||
12449 |
/* This is a root value */ |
||
12450 |
assert(ctx->frame == NULL); |
||
12451 |
|||
12452 |
/* |
||
12453 |
* This value will also be the overall result of JSON parsing |
||
12454 |
* (it's already owned by the `mjs_alt_json_parse()`) |
||
12455 |
*/ |
||
12456 |
ctx->result = v; |
||
12457 |
} |
||
12458 |
|||
12459 |
if (token->type == JSON_TYPE_OBJECT_START || |
||
12460 |
token->type == JSON_TYPE_ARRAY_START) { |
||
12461 |
/* New object or array has just started, so we need to allocate a frame |
||
12462 |
* for it */ |
||
12463 |
struct json_parse_frame *new_frame = alloc_json_frame(ctx, v); |
||
12464 |
new_frame->up = ctx->frame; |
||
12465 |
ctx->frame = new_frame; |
||
12466 |
} |
||
12467 |
} |
||
12468 |
|||
12469 |
mjs_disown(ctx->mjs, &v); |
||
12470 |
} |
||
12471 |
|||
12472 |
MJS_PRIVATE mjs_err_t |
||
12473 |
mjs_json_parse(struct mjs *mjs, const char *str, size_t len, mjs_val_t *res) { |
||
12474 |
struct json_parse_ctx *ctx = |
||
12475 |
(struct json_parse_ctx *) calloc(sizeof(struct json_parse_ctx), 1); |
||
12476 |
int json_res; |
||
12477 |
enum mjs_err rcode = MJS_OK; |
||
12478 |
|||
12479 |
ctx->mjs = mjs; |
||
12480 |
ctx->result = MJS_UNDEFINED; |
||
12481 |
ctx->frame = NULL; |
||
12482 |
ctx->rcode = MJS_OK; |
||
12483 |
|||
12484 |
mjs_own(mjs, &ctx->result); |
||
12485 |
|||
12486 |
{ |
||
12487 |
/* |
||
12488 |
* We have to reallocate the buffer before invoking json_walk, because |
||
12489 |
* frozen_cb can create new strings, which can result in the reallocation |
||
12490 |
* of mjs string mbuf, invalidating the `str` pointer. |
||
12491 |
*/ |
||
12492 |
char *stmp = malloc(len); |
||
12493 |
memcpy(stmp, str, len); |
||
12494 |
json_res = json_walk(stmp, len, frozen_cb, ctx); |
||
12495 |
free(stmp); |
||
12496 |
stmp = NULL; |
||
12497 |
|||
12498 |
/* str might have been invalidated, so null it out */ |
||
12499 |
str = NULL; |
||
12500 |
} |
||
12501 |
|||
12502 |
if (ctx->rcode != MJS_OK) { |
||
12503 |
rcode = ctx->rcode; |
||
12504 |
mjs_prepend_errorf(mjs, rcode, "invalid JSON string"); |
||
12505 |
} else if (json_res < 0) { |
||
12506 |
/* There was an error during parsing */ |
||
12507 |
rcode = MJS_TYPE_ERROR; |
||
12508 |
mjs_prepend_errorf(mjs, rcode, "invalid JSON string"); |
||
12509 |
} else { |
||
12510 |
/* Expression is parsed successfully */ |
||
12511 |
*res = ctx->result; |
||
12512 |
|||
12513 |
/* There should be no allocated frames */ |
||
12514 |
assert(ctx->frame == NULL); |
||
12515 |
} |
||
12516 |
|||
12517 |
if (rcode != MJS_OK) { |
||
12518 |
/* There might be some allocated frames in case of malformed JSON */ |
||
12519 |
while (ctx->frame != NULL) { |
||
12520 |
ctx->frame = free_json_frame(ctx, ctx->frame); |
||
12521 |
} |
||
12522 |
} |
||
12523 |
|||
12524 |
mjs_disown(mjs, &ctx->result); |
||
12525 |
free(ctx); |
||
12526 |
|||
12527 |
return rcode; |
||
12528 |
} |
||
12529 |
|||
12530 |
MJS_PRIVATE void mjs_op_json_stringify(struct mjs *mjs) { |
||
12531 |
mjs_val_t ret = MJS_UNDEFINED; |
||
12532 |
mjs_val_t val = mjs_arg(mjs, 0); |
||
12533 |
|||
12534 |
if (mjs_nargs(mjs) < 1) { |
||
12535 |
mjs_prepend_errorf(mjs, MJS_TYPE_ERROR, "missing a value to stringify"); |
||
12536 |
} else { |
||
12537 |
char *p = NULL; |
||
12538 |
if (mjs_json_stringify(mjs, val, NULL, 0, &p) == MJS_OK) { |
||
12539 |
ret = mjs_mk_string(mjs, p, ~0, 1 /* copy */); |
||
12540 |
free(p); |
||
12541 |
} |
||
12542 |
} |
||
12543 |
|||
12544 |
mjs_return(mjs, ret); |
||
12545 |
} |
||
12546 |
|||
12547 |
MJS_PRIVATE void mjs_op_json_parse(struct mjs *mjs) { |
||
12548 |
mjs_val_t ret = MJS_UNDEFINED; |
||
12549 |
mjs_val_t arg0 = mjs_arg(mjs, 0); |
||
12550 |
|||
12551 |
if (mjs_is_string(arg0)) { |
||
12552 |
size_t len; |
||
12553 |
const char *str = mjs_get_string(mjs, &arg0, &len); |
||
12554 |
mjs_json_parse(mjs, str, len, &ret); |
||
12555 |
} else { |
||
12556 |
mjs_prepend_errorf(mjs, MJS_TYPE_ERROR, "string argument required"); |
||
12557 |
} |
||
12558 |
|||
12559 |
mjs_return(mjs, ret); |
||
12560 |
} |
||
12561 |
#ifdef MJS_MODULE_LINES |
||
12562 |
#line 1 "mjs/src/mjs_main.c" |
||
12563 |
#endif |
||
12564 |
/* |
||
12565 |
* Copyright (c) 2014-2017 Cesanta Software Limited |
||
12566 |
* All rights reserved |
||
12567 |
*/ |
||
12568 |
|||
12569 |
|||
12570 |
// #include <dlfcn.h> |
||
12571 |
|||
12572 |
/* Amalgamated: #include "mjs/src/mjs_core.h" */ |
||
12573 |
/* Amalgamated: #include "mjs/src/mjs_exec.h" */ |
||
12574 |
/* Amalgamated: #include "mjs/src/mjs_internal.h" */ |
||
12575 |
/* Amalgamated: #include "mjs/src/mjs_primitive.h" */ |
||
12576 |
/* Amalgamated: #include "mjs/src/mjs_util.h" */ |
||
12577 |
|||
12578 |
// added to conform afl |
||
12579 |
78 |
char* read_input() { |
|
12580 |
78 |
int counter = 0; |
|
12581 |
78 |
char* chars = malloc(sizeof(char) * 1000); |
|
12582 |
78 |
char c = 0; |
|
12583 |
✓✓ | 705 |
while((c = fgetc(stdin)) != EOF){ |
12584 |
✗✓ | 549 |
if (counter == 1000) { |
12585 |
exit(1); |
||
12586 |
} |
||
12587 |
549 |
chars[counter++] = c; |
|
12588 |
} |
||
12589 |
78 |
chars[counter] = '\0'; |
|
12590 |
78 |
return chars; |
|
12591 |
} |
||
12592 |
// end adding |
||
12593 |
|||
12594 |
78 |
int main(int argc, char *argv[]) { |
|
12595 |
78 |
struct mjs *mjs = mjs_create(); |
|
12596 |
78 |
mjs_val_t res = MJS_UNDEFINED; |
|
12597 |
78 |
mjs_err_t err = MJS_OK; |
|
12598 |
int i; |
||
12599 |
|||
12600 |
✗✓✗✗ ✗✗ |
78 |
for (i = 1; i < argc && argv[i][0] == '-' && err == MJS_OK; i++) { |
12601 |
if (strcmp(argv[i], "-l") == 0 && i + 1 < argc) { |
||
12602 |
cs_log_set_level(atoi(argv[++i])); |
||
12603 |
} else if (strcmp(argv[i], "-j") == 0) { |
||
12604 |
mjs_set_generate_jsc(mjs, 1); |
||
12605 |
} else if (strcmp(argv[i], "-e") == 0 && i + 1 < argc) { |
||
12606 |
err = mjs_exec(mjs, argv[++i], &res); |
||
12607 |
} else if (strcmp(argv[i], "-f") == 0 && i + 1 < argc) { |
||
12608 |
err = mjs_exec_file(mjs, argv[++i], &res); |
||
12609 |
} else if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) { |
||
12610 |
printf("mJS (c) Cesanta, built: " __DATE__ "\n"); |
||
12611 |
printf("Usage:\n"); |
||
12612 |
printf("%s [OPTIONS] [js_file ...]\n", argv[0]); |
||
12613 |
printf("OPTIONS:\n"); |
||
12614 |
printf(" -e string - Execute JavaScript expression\n"); |
||
12615 |
printf(" -j - Enable code precompiling to .jsc files\n"); |
||
12616 |
printf(" -f js_file - Execute code from .js JavaScript file\n"); |
||
12617 |
printf(" -l level - Set debug level, from 0 to 5\n"); |
||
12618 |
return EXIT_SUCCESS; |
||
12619 |
} else { |
||
12620 |
fprintf(stderr, "Unknown flag: [%s]\n", argv[i]); |
||
12621 |
return EXIT_FAILURE; |
||
12622 |
} |
||
12623 |
} |
||
12624 |
|||
12625 |
✗✓ | 78 |
if (err != MJS_OK) { |
12626 |
mjs_print_error(mjs, stdout, NULL, 1 /* print_stack_trace */); |
||
12627 |
return EXIT_FAILURE; |
||
12628 |
} |
||
12629 |
78 |
err = mjs_exec(mjs, read_input(), &res); |
|
12630 |
|||
12631 |
✓✓ | 78 |
if (err == MJS_OK) { |
12632 |
66 |
mjs_fprintf(res, mjs, stdout); |
|
12633 |
66 |
putchar('\n'); |
|
12634 |
} else { |
||
12635 |
12 |
mjs_print_error(mjs, stdout, NULL, 1 /* print_stack_trace */); |
|
12636 |
} |
||
12637 |
78 |
mjs_destroy(mjs); |
|
12638 |
|||
12639 |
78 |
return EXIT_SUCCESS; |
|
12640 |
} |
||
12641 |
#ifdef MJS_MODULE_LINES |
||
12642 |
#line 1 "mjs/src/mjs_object.c" |
||
12643 |
#endif |
||
12644 |
/* |
||
12645 |
* Copyright (c) 2016 Cesanta Software Limited |
||
12646 |
* All rights reserved |
||
12647 |
*/ |
||
12648 |
|||
12649 |
/* Amalgamated: #include "mjs/src/mjs_object.h" */ |
||
12650 |
/* Amalgamated: #include "mjs/src/mjs_conversion.h" */ |
||
12651 |
/* Amalgamated: #include "mjs/src/mjs_core.h" */ |
||
12652 |
/* Amalgamated: #include "mjs/src/mjs_internal.h" */ |
||
12653 |
/* Amalgamated: #include "mjs/src/mjs_primitive.h" */ |
||
12654 |
/* Amalgamated: #include "mjs/src/mjs_string.h" */ |
||
12655 |
/* Amalgamated: #include "mjs/src/mjs_util.h" */ |
||
12656 |
|||
12657 |
/* Amalgamated: #include "common/mg_str.h" */ |
||
12658 |
|||
12659 |
235 |
MJS_PRIVATE mjs_val_t mjs_object_to_value(struct mjs_object *o) { |
|
12660 |
✗✓ | 235 |
if (o == NULL) { |
12661 |
return MJS_NULL; |
||
12662 |
} else { |
||
12663 |
235 |
return mjs_legit_pointer_to_value(o) | MJS_TAG_OBJECT; |
|
12664 |
} |
||
12665 |
} |
||
12666 |
|||
12667 |
3387 |
MJS_PRIVATE struct mjs_object *get_object_struct(mjs_val_t v) { |
|
12668 |
3387 |
struct mjs_object *ret = NULL; |
|
12669 |
✗✓ | 3387 |
if (mjs_is_null(v)) { |
12670 |
ret = NULL; |
||
12671 |
} else { |
||
12672 |
✗✓ | 3387 |
assert(mjs_is_object(v)); |
12673 |
3387 |
ret = (struct mjs_object *) get_ptr(v); |
|
12674 |
} |
||
12675 |
3387 |
return ret; |
|
12676 |
} |
||
12677 |
|||
12678 |
235 |
mjs_val_t mjs_mk_object(struct mjs *mjs) { |
|
12679 |
235 |
struct mjs_object *o = new_object(mjs); |
|
12680 |
✗✓ | 235 |
if (o == NULL) { |
12681 |
return MJS_NULL; |
||
12682 |
} |
||
12683 |
(void) mjs; |
||
12684 |
235 |
o->properties = NULL; |
|
12685 |
235 |
return mjs_object_to_value(o); |
|
12686 |
} |
||
12687 |
|||
12688 |
9514 |
int mjs_is_object(mjs_val_t v) { |
|
12689 |
✓✓✗✓ |
11986 |
return (v & MJS_TAG_MASK) == MJS_TAG_OBJECT || |
12690 |
2472 |
(v & MJS_TAG_MASK) == MJS_TAG_ARRAY; |
|
12691 |
} |
||
12692 |
|||
12693 |
1446 |
MJS_PRIVATE struct mjs_property *mjs_get_own_property(struct mjs *mjs, |
|
12694 |
mjs_val_t obj, |
||
12695 |
const char *name, |
||
12696 |
size_t len) { |
||
12697 |
struct mjs_property *p; |
||
12698 |
struct mjs_object *o; |
||
12699 |
|||
12700 |
✗✓ | 1446 |
if (!mjs_is_object(obj)) { |
12701 |
return NULL; |
||
12702 |
} |
||
12703 |
|||
12704 |
1446 |
o = get_object_struct(obj); |
|
12705 |
|||
12706 |
✓✓ | 1446 |
if (len <= 5) { |
12707 |
39 |
mjs_val_t ss = mjs_mk_string(mjs, name, len, 1); |
|
12708 |
✓✓ | 624 |
for (p = o->properties; p != NULL; p = p->next) { |
12709 |
✗✓ | 585 |
if (p->name == ss) return p; |
12710 |
} |
||
12711 |
} else { |
||
12712 |
✓✓ | 9720 |
for (p = o->properties; p != NULL; p = p->next) { |
12713 |
✗✓ | 8313 |
if (mjs_strcmp(mjs, &p->name, name, len) == 0) return p; |
12714 |
} |
||
12715 |
1407 |
return p; |
|
12716 |
} |
||
12717 |
|||
12718 |
39 |
return NULL; |
|
12719 |
} |
||
12720 |
|||
12721 |
41 |
MJS_PRIVATE struct mjs_property *mjs_get_own_property_v(struct mjs *mjs, |
|
12722 |
mjs_val_t obj, |
||
12723 |
mjs_val_t key) { |
||
12724 |
size_t n; |
||
12725 |
41 |
char *s = NULL; |
|
12726 |
41 |
int need_free = 0; |
|
12727 |
41 |
struct mjs_property *p = NULL; |
|
12728 |
41 |
mjs_err_t err = mjs_to_string(mjs, &key, &s, &n, &need_free); |
|
12729 |
✓✗ | 41 |
if (err == MJS_OK) { |
12730 |
41 |
p = mjs_get_own_property(mjs, obj, s, n); |
|
12731 |
} |
||
12732 |
✗✓ | 41 |
if (need_free) free(s); |
12733 |
41 |
return p; |
|
12734 |
} |
||
12735 |
|||
12736 |
1405 |
MJS_PRIVATE struct mjs_property *mjs_mk_property(struct mjs *mjs, |
|
12737 |
mjs_val_t name, |
||
12738 |
mjs_val_t value) { |
||
12739 |
1405 |
struct mjs_property *p = new_property(mjs); |
|
12740 |
1405 |
p->next = NULL; |
|
12741 |
1405 |
p->name = name; |
|
12742 |
1405 |
p->value = value; |
|
12743 |
1405 |
return p; |
|
12744 |
} |
||
12745 |
|||
12746 |
mjs_val_t mjs_get(struct mjs *mjs, mjs_val_t obj, const char *name, |
||
12747 |
size_t name_len) { |
||
12748 |
struct mjs_property *p; |
||
12749 |
|||
12750 |
if (name_len == (size_t) ~0) { |
||
12751 |
name_len = strlen(name); |
||
12752 |
} |
||
12753 |
|||
12754 |
p = mjs_get_own_property(mjs, obj, name, name_len); |
||
12755 |
if (p == NULL) { |
||
12756 |
return MJS_UNDEFINED; |
||
12757 |
} else { |
||
12758 |
return p->value; |
||
12759 |
} |
||
12760 |
} |
||
12761 |
|||
12762 |
mjs_val_t mjs_get_v(struct mjs *mjs, mjs_val_t obj, mjs_val_t name) { |
||
12763 |
size_t n; |
||
12764 |
char *s = NULL; |
||
12765 |
int need_free = 0; |
||
12766 |
mjs_val_t ret = MJS_UNDEFINED; |
||
12767 |
|||
12768 |
mjs_err_t err = mjs_to_string(mjs, &name, &s, &n, &need_free); |
||
12769 |
|||
12770 |
if (err == MJS_OK) { |
||
12771 |
/* Successfully converted name value to string: get the property */ |
||
12772 |
ret = mjs_get(mjs, obj, s, n); |
||
12773 |
} |
||
12774 |
|||
12775 |
if (need_free) { |
||
12776 |
free(s); |
||
12777 |
s = NULL; |
||
12778 |
} |
||
12779 |
return ret; |
||
12780 |
} |
||
12781 |
|||
12782 |
mjs_val_t mjs_get_v_proto(struct mjs *mjs, mjs_val_t obj, mjs_val_t key) { |
||
12783 |
struct mjs_property *p; |
||
12784 |
mjs_val_t pn = mjs_mk_string(mjs, MJS_PROTO_PROP_NAME, ~0, 1); |
||
12785 |
if ((p = mjs_get_own_property_v(mjs, obj, key)) != NULL) return p->value; |
||
12786 |
if ((p = mjs_get_own_property_v(mjs, obj, pn)) == NULL) return MJS_UNDEFINED; |
||
12787 |
return mjs_get_v_proto(mjs, p->value, key); |
||
12788 |
} |
||
12789 |
|||
12790 |
1404 |
mjs_err_t mjs_set(struct mjs *mjs, mjs_val_t obj, const char *name, |
|
12791 |
size_t name_len, mjs_val_t val) { |
||
12792 |
1404 |
return mjs_set_internal(mjs, obj, MJS_UNDEFINED, (char *) name, name_len, |
|
12793 |
val); |
||
12794 |
} |
||
12795 |
|||
12796 |
1 |
mjs_err_t mjs_set_v(struct mjs *mjs, mjs_val_t obj, mjs_val_t name, |
|
12797 |
mjs_val_t val) { |
||
12798 |
1 |
return mjs_set_internal(mjs, obj, name, NULL, 0, val); |
|
12799 |
} |
||
12800 |
|||
12801 |
1405 |
MJS_PRIVATE mjs_err_t mjs_set_internal(struct mjs *mjs, mjs_val_t obj, |
|
12802 |
mjs_val_t name_v, char *name, |
||
12803 |
size_t name_len, mjs_val_t val) { |
||
12804 |
1405 |
mjs_err_t rcode = MJS_OK; |
|
12805 |
|||
12806 |
struct mjs_property *p; |
||
12807 |
|||
12808 |
1405 |
int need_free = 0; |
|
12809 |
|||
12810 |
✓✓ | 1405 |
if (name == NULL) { |
12811 |
/* Pointer was not provided, so obtain one from the name_v. */ |
||
12812 |
1 |
rcode = mjs_to_string(mjs, &name_v, &name, &name_len, &need_free); |
|
12813 |
✗✓ | 1 |
if (rcode != MJS_OK) { |
12814 |
goto clean; |
||
12815 |
} |
||
12816 |
} else { |
||
12817 |
/* |
||
12818 |
* Pointer was provided, so we ignore name_v. Here we set it to undefined, |
||
12819 |
* and the actual value will be calculated later if needed. |
||
12820 |
*/ |
||
12821 |
1404 |
name_v = MJS_UNDEFINED; |
|
12822 |
} |
||
12823 |
|||
12824 |
1405 |
p = mjs_get_own_property(mjs, obj, name, name_len); |
|
12825 |
|||
12826 |
✓✗ | 1405 |
if (p == NULL) { |
12827 |
struct mjs_object *o; |
||
12828 |
✗✓ | 1405 |
if (!mjs_is_object(obj)) { |
12829 |
return MJS_REFERENCE_ERROR; |
||
12830 |
} |
||
12831 |
|||
12832 |
/* |
||
12833 |
* name_v might be not a string here. In this case, we need to create a new |
||
12834 |
* `name_v`, which will be a string. |
||
12835 |
*/ |
||
12836 |
✓✓ | 1405 |
if (!mjs_is_string(name_v)) { |
12837 |
1404 |
name_v = mjs_mk_string(mjs, name, name_len, 1); |
|
12838 |
} |
||
12839 |
|||
12840 |
1405 |
p = mjs_mk_property(mjs, name_v, val); |
|
12841 |
|||
12842 |
1405 |
o = get_object_struct(obj); |
|
12843 |
1405 |
p->next = o->properties; |
|
12844 |
1405 |
o->properties = p; |
|
12845 |
} |
||
12846 |
|||
12847 |
1405 |
p->value = val; |
|
12848 |
|||
12849 |
clean: |
||
12850 |
✗✓ | 1405 |
if (need_free) { |
12851 |
free(name); |
||
12852 |
name = NULL; |
||
12853 |
} |
||
12854 |
1405 |
return rcode; |
|
12855 |
} |
||
12856 |
|||
12857 |
MJS_PRIVATE void mjs_destroy_property(struct mjs_property **p) { |
||
12858 |
*p = NULL; |
||
12859 |
} |
||
12860 |
|||
12861 |
/* |
||
12862 |
* See comments in `object_public.h` |
||
12863 |
*/ |
||
12864 |
int mjs_del(struct mjs *mjs, mjs_val_t obj, const char *name, size_t len) { |
||
12865 |
struct mjs_property *prop, *prev; |
||
12866 |
|||
12867 |
if (!mjs_is_object(obj)) { |
||
12868 |
return -1; |
||
12869 |
} |
||
12870 |
if (len == (size_t) ~0) { |
||
12871 |
len = strlen(name); |
||
12872 |
} |
||
12873 |
for (prev = NULL, prop = get_object_struct(obj)->properties; prop != NULL; |
||
12874 |
prev = prop, prop = prop->next) { |
||
12875 |
size_t n; |
||
12876 |
const char *s = mjs_get_string(mjs, &prop->name, &n); |
||
12877 |
if (n == len && strncmp(s, name, len) == 0) { |
||
12878 |
if (prev) { |
||
12879 |
prev->next = prop->next; |
||
12880 |
} else { |
||
12881 |
get_object_struct(obj)->properties = prop->next; |
||
12882 |
} |
||
12883 |
mjs_destroy_property(&prop); |
||
12884 |
return 0; |
||
12885 |
} |
||
12886 |
} |
||
12887 |
return -1; |
||
12888 |
} |
||
12889 |
|||
12890 |
mjs_val_t mjs_next(struct mjs *mjs, mjs_val_t obj, mjs_val_t *iterator) { |
||
12891 |
struct mjs_property *p = NULL; |
||
12892 |
mjs_val_t key = MJS_UNDEFINED; |
||
12893 |
|||
12894 |
if (*iterator == MJS_UNDEFINED) { |
||
12895 |
struct mjs_object *o = get_object_struct(obj); |
||
12896 |
p = o->properties; |
||
12897 |
} else { |
||
12898 |
p = ((struct mjs_property *) get_ptr(*iterator))->next; |
||
12899 |
} |
||
12900 |
|||
12901 |
if (p == NULL) { |
||
12902 |
*iterator = MJS_UNDEFINED; |
||
12903 |
} else { |
||
12904 |
key = p->name; |
||
12905 |
*iterator = mjs_mk_foreign(mjs, p); |
||
12906 |
} |
||
12907 |
|||
12908 |
return key; |
||
12909 |
} |
||
12910 |
|||
12911 |
MJS_PRIVATE void mjs_op_create_object(struct mjs *mjs) { |
||
12912 |
mjs_val_t ret = MJS_UNDEFINED; |
||
12913 |
mjs_val_t proto_v = mjs_arg(mjs, 0); |
||
12914 |
|||
12915 |
if (!mjs_check_arg(mjs, 0, "proto", MJS_TYPE_OBJECT_GENERIC, &proto_v)) { |
||
12916 |
goto clean; |
||
12917 |
} |
||
12918 |
|||
12919 |
ret = mjs_mk_object(mjs); |
||
12920 |
mjs_set(mjs, ret, MJS_PROTO_PROP_NAME, ~0, proto_v); |
||
12921 |
|||
12922 |
clean: |
||
12923 |
mjs_return(mjs, ret); |
||
12924 |
} |
||
12925 |
|||
12926 |
mjs_val_t mjs_struct_to_obj(struct mjs *mjs, const void *base, |
||
12927 |
const struct mjs_c_struct_member *def) { |
||
12928 |
mjs_val_t obj; |
||
12929 |
if (base == NULL || def == NULL) return MJS_UNDEFINED; |
||
12930 |
obj = mjs_mk_object(mjs); |
||
12931 |
mjs_own(mjs, &obj); /* Pin the object while it is being built */ |
||
12932 |
for (; def->name != NULL; def++) { |
||
12933 |
const char *ptr = (const char *) base + def->offset; |
||
12934 |
switch (def->type) { |
||
12935 |
case MJS_FFI_CTYPE_INT: { |
||
12936 |
double value = (double) (*(int *) ptr); |
||
12937 |
mjs_set(mjs, obj, def->name, ~0, mjs_mk_number(mjs, value)); |
||
12938 |
break; |
||
12939 |
} |
||
12940 |
case MJS_FFI_CTYPE_CHAR_PTR: { |
||
12941 |
const char *value = *(const char **) ptr; |
||
12942 |
mjs_set(mjs, obj, def->name, ~0, mjs_mk_string(mjs, value, ~0, 1)); |
||
12943 |
break; |
||
12944 |
} |
||
12945 |
case MJS_FFI_CTYPE_DOUBLE: { |
||
12946 |
mjs_set(mjs, obj, def->name, ~0, mjs_mk_number(mjs, *(double *) ptr)); |
||
12947 |
break; |
||
12948 |
} |
||
12949 |
case MJS_FFI_CTYPE_STRUCT_MG_STR: { |
||
12950 |
const struct mg_str *s = (const struct mg_str *) ptr; |
||
12951 |
mjs_set(mjs, obj, def->name, ~0, mjs_mk_string(mjs, s->p, s->len, 1)); |
||
12952 |
break; |
||
12953 |
} |
||
12954 |
case MJS_FFI_CTYPE_STRUCT_MG_STR_PTR: { |
||
12955 |
const struct mg_str *s = *(const struct mg_str **) ptr; |
||
12956 |
mjs_set(mjs, obj, def->name, ~0, mjs_mk_string(mjs, s->p, s->len, 1)); |
||
12957 |
break; |
||
12958 |
} |
||
12959 |
case MJS_FFI_CTYPE_FLOAT: { |
||
12960 |
float value = *(float *) ptr; |
||
12961 |
mjs_set(mjs, obj, def->name, ~0, mjs_mk_number(mjs, value)); |
||
12962 |
break; |
||
12963 |
} |
||
12964 |
case MJS_FFI_CTYPE_VOID_PTR: { |
||
12965 |
mjs_set(mjs, obj, def->name, ~0, mjs_mk_foreign(mjs, *(void **) ptr)); |
||
12966 |
break; |
||
12967 |
} |
||
12968 |
case MJS_FFI_CTYPE_BOOL: { |
||
12969 |
mjs_set(mjs, obj, def->name, ~0, mjs_mk_boolean(mjs, *(bool *) ptr)); |
||
12970 |
break; |
||
12971 |
} |
||
12972 |
default: |
||
12973 |
obj = MJS_UNDEFINED; |
||
12974 |
goto clean; |
||
12975 |
} |
||
12976 |
} |
||
12977 |
clean: |
||
12978 |
mjs_disown(mjs, &obj); |
||
12979 |
return obj; |
||
12980 |
} |
||
12981 |
#ifdef MJS_MODULE_LINES |
||
12982 |
#line 1 "mjs/src/mjs_parser.c" |
||
12983 |
#endif |
||
12984 |
/* |
||
12985 |
* Copyright (c) 2017 Cesanta Software Limited |
||
12986 |
* All rights reserved |
||
12987 |
*/ |
||
12988 |
|||
12989 |
/* Amalgamated: #include "common/cs_varint.h" */ |
||
12990 |
|||
12991 |
/* Amalgamated: #include "mjs/src/mjs_bcode.h" */ |
||
12992 |
/* Amalgamated: #include "mjs/src/mjs_core.h" */ |
||
12993 |
/* Amalgamated: #include "mjs/src/mjs_internal.h" */ |
||
12994 |
/* Amalgamated: #include "mjs/src/mjs_parser.h" */ |
||
12995 |
/* Amalgamated: #include "mjs/src/mjs_string.h" */ |
||
12996 |
/* Amalgamated: #include "mjs/src/mjs_tok.h" */ |
||
12997 |
|||
12998 |
#ifndef MAX_TOKS_IN_EXPR |
||
12999 |
#define MAX_TOKS_IN_EXPR 40 |
||
13000 |
#endif |
||
13001 |
|||
13002 |
#define FAIL_ERR(p, code) \ |
||
13003 |
do { \ |
||
13004 |
mjs_set_errorf(p->mjs, code, "parse error at line %d: [%.*s]", p->line_no, \ |
||
13005 |
10, p->tok.ptr); \ |
||
13006 |
mjs_print_error(p->mjs, stdout, NULL, 1 /* print_stack_trace */); \ |
||
13007 |
exit(EXIT_FAILURE); \ |
||
13008 |
/*return code;*/ \ |
||
13009 |
} while (0) |
||
13010 |
|||
13011 |
#define pnext1(p) \ |
||
13012 |
do { \ |
||
13013 |
LOG(LL_VERBOSE_DEBUG, (" PNEXT %d", __LINE__)); \ |
||
13014 |
pnext(p); \ |
||
13015 |
} while (0) |
||
13016 |
|||
13017 |
#define SYNTAX_ERROR(p) FAIL_ERR(p, MJS_SYNTAX_ERROR) |
||
13018 |
#undef EXPECT |
||
13019 |
#define EXPECT(p, t) \ |
||
13020 |
if ((p)->tok.tok != (t)) \ |
||
13021 |
SYNTAX_ERROR(p); \ |
||
13022 |
else \ |
||
13023 |
pnext1(p); |
||
13024 |
|||
13025 |
static mjs_err_t parse_statement(struct pstate *p); |
||
13026 |
static mjs_err_t parse_expr(struct pstate *p); |
||
13027 |
|||
13028 |
79 |
static int ptest(struct pstate *p) { |
|
13029 |
79 |
struct pstate saved = *p; |
|
13030 |
79 |
int tok = pnext(p); |
|
13031 |
79 |
*p = saved; |
|
13032 |
79 |
return tok; |
|
13033 |
} |
||
13034 |
|||
13035 |
static int s_unary_ops[] = {TOK_NOT, TOK_TILDA, TOK_PLUS_PLUS, TOK_MINUS_MINUS, |
||
13036 |
TOK_KEYWORD_TYPEOF, TOK_MINUS, TOK_PLUS, TOK_EOF}; |
||
13037 |
static int s_comparison_ops[] = {TOK_LT, TOK_LE, TOK_GT, TOK_GE, TOK_EOF}; |
||
13038 |
static int s_postfix_ops[] = {TOK_PLUS_PLUS, TOK_MINUS_MINUS, TOK_EOF}; |
||
13039 |
static int s_equality_ops[] = {TOK_EQ, TOK_NE, TOK_EQ_EQ, TOK_NE_NE, TOK_EOF}; |
||
13040 |
static int s_assign_ops[] = { |
||
13041 |
TOK_ASSIGN, TOK_PLUS_ASSIGN, TOK_MINUS_ASSIGN, TOK_MUL_ASSIGN, |
||
13042 |
TOK_DIV_ASSIGN, TOK_REM_ASSIGN, TOK_LSHIFT_ASSIGN, TOK_RSHIFT_ASSIGN, |
||
13043 |
TOK_URSHIFT_ASSIGN, TOK_AND_ASSIGN, TOK_XOR_ASSIGN, TOK_OR_ASSIGN, |
||
13044 |
TOK_EOF}; |
||
13045 |
|||
13046 |
2067 |
static int findtok(int *toks, int tok) { |
|
13047 |
2067 |
int i = 0; |
|
13048 |
✓✓✓✓ |
2067 |
while (tok != toks[i] && toks[i] != TOK_EOF) i++; |
13049 |
2067 |
return toks[i]; |
|
13050 |
} |
||
13051 |
|||
13052 |
68 |
static void emit_op(struct pstate *pstate, int tok) { |
|
13053 |
✓✗✗✓ |
68 |
assert(tok >= 0 && tok <= 255); |
13054 |
68 |
emit_byte(pstate, OP_EXPR); |
|
13055 |
68 |
emit_byte(pstate, (uint8_t) tok); |
|
13056 |
68 |
} |
|
13057 |
|||
13058 |
#define BINOP_STACK_FRAME_SIZE 16 |
||
13059 |
#define STACK_LIMIT 8192 |
||
13060 |
|||
13061 |
// Intentionally left as macro rather than a function, to let the |
||
13062 |
// compiler to inline calls and mimimize runtime stack usage. |
||
13063 |
#define PARSE_LTR_BINOP(p, f1, f2, ops, prev_op) \ |
||
13064 |
do { \ |
||
13065 |
mjs_err_t res = MJS_OK; \ |
||
13066 |
p->depth++; \ |
||
13067 |
if (p->depth > (STACK_LIMIT / BINOP_STACK_FRAME_SIZE)) { \ |
||
13068 |
mjs_set_errorf(p->mjs, MJS_SYNTAX_ERROR, "parser stack overflow"); \ |
||
13069 |
res = MJS_SYNTAX_ERROR; \ |
||
13070 |
goto binop_clean; \ |
||
13071 |
} \ |
||
13072 |
if ((res = f1(p, TOK_EOF)) != MJS_OK) goto binop_clean; \ |
||
13073 |
if (prev_op != TOK_EOF) emit_op(p, prev_op); \ |
||
13074 |
if (findtok(ops, p->tok.tok) != TOK_EOF) { \ |
||
13075 |
int op = p->tok.tok; \ |
||
13076 |
size_t off_if = 0; \ |
||
13077 |
/* For AND/OR, implement short-circuit evaluation */ \ |
||
13078 |
if (ops[0] == TOK_LOGICAL_AND || ops[0] == TOK_LOGICAL_OR) { \ |
||
13079 |
emit_byte(p, \ |
||
13080 |
(uint8_t)(ops[0] == TOK_LOGICAL_AND ? OP_JMP_NEUTRAL_FALSE \ |
||
13081 |
: OP_JMP_NEUTRAL_TRUE)); \ |
||
13082 |
off_if = p->cur_idx; \ |
||
13083 |
emit_init_offset(p); \ |
||
13084 |
/* No need to emit TOK_LOGICAL_AND and TOK_LOGICAL_OR: */ \ |
||
13085 |
/* Just drop the first value, and evaluate the second one. */ \ |
||
13086 |
emit_byte(p, (uint8_t) OP_DROP); \ |
||
13087 |
op = TOK_EOF; \ |
||
13088 |
} \ |
||
13089 |
pnext1(p); \ |
||
13090 |
if ((res = f2(p, op)) != MJS_OK) goto binop_clean; \ |
||
13091 |
\ |
||
13092 |
if (off_if != 0) { \ |
||
13093 |
mjs_bcode_insert_offset(p, p->mjs, off_if, \ |
||
13094 |
p->cur_idx - off_if - MJS_INIT_OFFSET_SIZE); \ |
||
13095 |
} \ |
||
13096 |
} \ |
||
13097 |
binop_clean: \ |
||
13098 |
p->depth--; \ |
||
13099 |
return res; \ |
||
13100 |
} while (0) |
||
13101 |
|||
13102 |
#define PARSE_RTL_BINOP(p, f1, f2, ops, prev_op) \ |
||
13103 |
do { \ |
||
13104 |
mjs_err_t res = MJS_OK; \ |
||
13105 |
(void) prev_op; \ |
||
13106 |
if ((res = f1(p, TOK_EOF)) != MJS_OK) return res; \ |
||
13107 |
if (findtok(ops, p->tok.tok) != TOK_EOF) { \ |
||
13108 |
int op = p->tok.tok; \ |
||
13109 |
pnext1(p); \ |
||
13110 |
if ((res = f2(p, TOK_EOF)) != MJS_OK) return res; \ |
||
13111 |
emit_op(p, op); \ |
||
13112 |
} \ |
||
13113 |
return res; \ |
||
13114 |
} while (0) |
||
13115 |
|||
13116 |
#if MJS_INIT_OFFSET_SIZE > 0 |
||
13117 |
10 |
static void emit_init_offset(struct pstate *p) { |
|
13118 |
size_t i; |
||
13119 |
✓✓ | 20 |
for (i = 0; i < MJS_INIT_OFFSET_SIZE; i++) { |
13120 |
10 |
emit_byte(p, 0); |
|
13121 |
} |
||
13122 |
10 |
} |
|
13123 |
#else |
||
13124 |
static void emit_init_offset(struct pstate *p) { |
||
13125 |
(void) p; |
||
13126 |
} |
||
13127 |
#endif |
||
13128 |
|||
13129 |
83 |
static mjs_err_t parse_statement_list(struct pstate *p, int et) { |
|
13130 |
83 |
mjs_err_t res = MJS_OK; |
|
13131 |
83 |
int drop = 0; |
|
13132 |
✗✓ | 83 |
pnext1(p); |
13133 |
✓✓✓✓ ✓✓ |
272 |
while (res == MJS_OK && p->tok.tok != TOK_EOF && p->tok.tok != et) { |
13134 |
✓✓ | 106 |
if (drop) emit_byte(p, OP_DROP); |
13135 |
106 |
res = parse_statement(p); |
|
13136 |
106 |
drop = 1; |
|
13137 |
✗✓✓✓ |
106 |
while (p->tok.tok == TOK_SEMICOLON) pnext1(p); |
13138 |
} |
||
13139 |
|||
13140 |
/* |
||
13141 |
* Client code expects statement list to contain a value, so if the statement |
||
13142 |
* list was empty, push `undefined`. |
||
13143 |
*/ |
||
13144 |
✓✓ | 83 |
if (!drop) { |
13145 |
3 |
emit_byte(p, OP_PUSH_UNDEF); |
|
13146 |
} |
||
13147 |
83 |
return res; |
|
13148 |
} |
||
13149 |
|||
13150 |
5 |
static mjs_err_t parse_block(struct pstate *p, int mkscope) { |
|
13151 |
5 |
mjs_err_t res = MJS_OK; |
|
13152 |
5 |
p->depth++; |
|
13153 |
✗✓ | 5 |
if (p->depth > (STACK_LIMIT / BINOP_STACK_FRAME_SIZE)) { |
13154 |
mjs_set_errorf(p->mjs, MJS_SYNTAX_ERROR, "parser stack overflow"); |
||
13155 |
res = MJS_SYNTAX_ERROR; |
||
13156 |
return res; |
||
13157 |
} |
||
13158 |
✗✓ | 5 |
LOG(LL_VERBOSE_DEBUG, ("[%.*s]", 10, p->tok.ptr)); |
13159 |
✓✗ | 5 |
if (mkscope) emit_byte(p, OP_NEW_SCOPE); |
13160 |
5 |
res = parse_statement_list(p, TOK_CLOSE_CURLY); |
|
13161 |
✗✓✗✓ |
5 |
EXPECT(p, TOK_CLOSE_CURLY); |
13162 |
✓✗ | 5 |
if (mkscope) emit_byte(p, OP_DEL_SCOPE); |
13163 |
5 |
return res; |
|
13164 |
} |
||
13165 |
|||
13166 |
static mjs_err_t parse_function(struct pstate *p) { |
||
13167 |
size_t prologue, off; |
||
13168 |
int arg_no = 0; |
||
13169 |
int name_provided = 0; |
||
13170 |
mjs_err_t res = MJS_OK; |
||
13171 |
|||
13172 |
EXPECT(p, TOK_KEYWORD_FUNCTION); |
||
13173 |
|||
13174 |
if (p->tok.tok == TOK_IDENT) { |
||
13175 |
/* Function name was provided */ |
||
13176 |
struct tok tmp = p->tok; |
||
13177 |
name_provided = 1; |
||
13178 |
emit_byte(p, OP_PUSH_STR); |
||
13179 |
emit_str(p, tmp.ptr, tmp.len); |
||
13180 |
emit_byte(p, OP_PUSH_SCOPE); |
||
13181 |
emit_byte(p, OP_CREATE); |
||
13182 |
emit_byte(p, OP_PUSH_STR); |
||
13183 |
emit_str(p, tmp.ptr, tmp.len); |
||
13184 |
emit_byte(p, OP_FIND_SCOPE); |
||
13185 |
pnext1(p); |
||
13186 |
} |
||
13187 |
|||
13188 |
emit_byte(p, OP_JMP); |
||
13189 |
off = p->cur_idx; |
||
13190 |
emit_init_offset(p); |
||
13191 |
|||
13192 |
prologue = p->cur_idx; |
||
13193 |
|||
13194 |
EXPECT(p, TOK_OPEN_PAREN); |
||
13195 |
emit_byte(p, OP_NEW_SCOPE); |
||
13196 |
// Emit names of function arguments |
||
13197 |
while (p->tok.tok != TOK_CLOSE_PAREN) { |
||
13198 |
if (p->tok.tok != TOK_IDENT) SYNTAX_ERROR(p); |
||
13199 |
emit_byte(p, OP_SET_ARG); |
||
13200 |
emit_int(p, arg_no); |
||
13201 |
arg_no++; |
||
13202 |
emit_str(p, p->tok.ptr, p->tok.len); |
||
13203 |
if (ptest(p) == TOK_COMMA) pnext1(p); |
||
13204 |
pnext1(p); |
||
13205 |
} |
||
13206 |
EXPECT(p, TOK_CLOSE_PAREN); |
||
13207 |
if ((res = parse_block(p, 0)) != MJS_OK) return res; |
||
13208 |
emit_byte(p, OP_RETURN); |
||
13209 |
prologue += mjs_bcode_insert_offset(p, p->mjs, off, |
||
13210 |
p->cur_idx - off - MJS_INIT_OFFSET_SIZE); |
||
13211 |
emit_byte(p, OP_PUSH_FUNC); |
||
13212 |
emit_int(p, p->cur_idx - 1 /* OP_PUSH_FUNC */ - prologue); |
||
13213 |
if (name_provided) { |
||
13214 |
emit_op(p, TOK_ASSIGN); |
||
13215 |
} |
||
13216 |
|||
13217 |
return res; |
||
13218 |
} |
||
13219 |
|||
13220 |
2 |
static mjs_err_t parse_object_literal(struct pstate *p) { |
|
13221 |
2 |
mjs_err_t res = MJS_OK; |
|
13222 |
✗✓✗✓ |
2 |
EXPECT(p, TOK_OPEN_CURLY); |
13223 |
2 |
emit_byte(p, OP_PUSH_OBJ); |
|
13224 |
✗✓ | 2 |
while (p->tok.tok != TOK_CLOSE_CURLY) { |
13225 |
if (p->tok.tok != TOK_IDENT && p->tok.tok != TOK_STR) SYNTAX_ERROR(p); |
||
13226 |
emit_byte(p, OP_DUP); |
||
13227 |
emit_byte(p, OP_PUSH_STR); |
||
13228 |
emit_str(p, p->tok.ptr, p->tok.len); |
||
13229 |
emit_byte(p, OP_SWAP); |
||
13230 |
pnext1(p); |
||
13231 |
EXPECT(p, TOK_COLON); |
||
13232 |
if ((res = parse_expr(p)) != MJS_OK) return res; |
||
13233 |
emit_op(p, TOK_ASSIGN); |
||
13234 |
emit_byte(p, OP_DROP); |
||
13235 |
if (p->tok.tok == TOK_COMMA) { |
||
13236 |
pnext1(p); |
||
13237 |
} else if (p->tok.tok != TOK_CLOSE_CURLY) { |
||
13238 |
SYNTAX_ERROR(p); |
||
13239 |
} |
||
13240 |
} |
||
13241 |
2 |
return res; |
|
13242 |
} |
||
13243 |
|||
13244 |
1 |
static mjs_err_t parse_array_literal(struct pstate *p) { |
|
13245 |
1 |
mjs_err_t res = MJS_OK; |
|
13246 |
✗✓✗✓ |
1 |
EXPECT(p, TOK_OPEN_BRACKET); |
13247 |
1 |
emit_byte(p, OP_PUSH_ARRAY); |
|
13248 |
✓✓ | 4 |
while (p->tok.tok != TOK_CLOSE_BRACKET) { |
13249 |
2 |
emit_byte(p, OP_DUP); |
|
13250 |
✗✓ | 2 |
if ((res = parse_expr(p)) != MJS_OK) return res; |
13251 |
2 |
emit_byte(p, OP_APPEND); |
|
13252 |
✗✓✗✗ |
2 |
if (p->tok.tok == TOK_COMMA) pnext1(p); |
13253 |
} |
||
13254 |
1 |
return res; |
|
13255 |
} |
||
13256 |
|||
13257 |
153 |
static enum mjs_err parse_literal(struct pstate *p, const struct tok *t) { |
|
13258 |
153 |
struct mbuf *bcode_gen = &p->mjs->bcode_gen; |
|
13259 |
153 |
enum mjs_err res = MJS_OK; |
|
13260 |
153 |
int tok = t->tok; |
|
13261 |
✗✓ | 153 |
LOG(LL_VERBOSE_DEBUG, ("[%.*s] %p", p->tok.len, p->tok.ptr, (void *) &t)); |
13262 |
✓✓✓✓ ✓✓✓✓ ✓✓✗✓ ✗ |
153 |
switch (t->tok) { |
13263 |
case TOK_KEYWORD_FALSE: |
||
13264 |
2 |
emit_byte(p, OP_PUSH_FALSE); |
|
13265 |
2 |
break; |
|
13266 |
case TOK_KEYWORD_TRUE: |
||
13267 |
1 |
emit_byte(p, OP_PUSH_TRUE); |
|
13268 |
1 |
break; |
|
13269 |
case TOK_KEYWORD_UNDEFINED: |
||
13270 |
1 |
emit_byte(p, OP_PUSH_UNDEF); |
|
13271 |
1 |
break; |
|
13272 |
case TOK_KEYWORD_NULL: |
||
13273 |
2 |
emit_byte(p, OP_PUSH_NULL); |
|
13274 |
2 |
break; |
|
13275 |
case TOK_IDENT: { |
||
13276 |
74 |
int prev_tok = p->prev_tok; |
|
13277 |
74 |
int next_tok = ptest(p); |
|
13278 |
74 |
emit_byte(p, OP_PUSH_STR); |
|
13279 |
74 |
emit_str(p, t->ptr, t->len); |
|
13280 |
✓✓ | 74 |
emit_byte(p, (uint8_t)(prev_tok == TOK_DOT ? OP_SWAP : OP_FIND_SCOPE)); |
13281 |
✓✓✓✓ |
140 |
if (!findtok(s_assign_ops, next_tok) && |
13282 |
✓✓ | 130 |
!findtok(s_postfix_ops, next_tok) && |
13283 |
/* TODO(dfrank): fix: it doesn't work for prefix ops */ |
||
13284 |
64 |
!findtok(s_postfix_ops, prev_tok)) { |
|
13285 |
63 |
emit_byte(p, OP_GET); |
|
13286 |
} |
||
13287 |
74 |
break; |
|
13288 |
} |
||
13289 |
case TOK_NUM: { |
||
13290 |
42 |
double iv, d = strtod(t->ptr, NULL); |
|
13291 |
42 |
unsigned long uv = strtoul(t->ptr + 2, NULL, 16); |
|
13292 |
✓✓✓✓ |
42 |
if (t->ptr[0] == '0' && t->ptr[1] == 'x') d = uv; |
13293 |
✓✗ | 42 |
if (modf(d, &iv) == 0) { |
13294 |
42 |
emit_byte(p, OP_PUSH_INT); |
|
13295 |
42 |
emit_int(p, (int64_t) d); |
|
13296 |
} else { |
||
13297 |
emit_byte(p, OP_PUSH_DBL); |
||
13298 |
emit_str(p, t->ptr, t->len); |
||
13299 |
} |
||
13300 |
42 |
break; |
|
13301 |
} |
||
13302 |
case TOK_STR: { |
||
13303 |
size_t oldlen; |
||
13304 |
25 |
emit_byte(p, OP_PUSH_STR); |
|
13305 |
25 |
oldlen = bcode_gen->len; |
|
13306 |
25 |
embed_string(bcode_gen, p->cur_idx, t->ptr, t->len, EMBSTR_UNESCAPE); |
|
13307 |
25 |
p->cur_idx += bcode_gen->len - oldlen; |
|
13308 |
25 |
} break; |
|
13309 |
case TOK_OPEN_BRACKET: |
||
13310 |
1 |
res = parse_array_literal(p); |
|
13311 |
1 |
break; |
|
13312 |
case TOK_OPEN_CURLY: |
||
13313 |
2 |
res = parse_object_literal(p); |
|
13314 |
2 |
break; |
|
13315 |
case TOK_OPEN_PAREN: |
||
13316 |
✗✓ | 2 |
pnext1(p); |
13317 |
2 |
res = parse_expr(p); |
|
13318 |
✗✓ | 2 |
if (p->tok.tok != TOK_CLOSE_PAREN) SYNTAX_ERROR(p); |
13319 |
2 |
break; |
|
13320 |
case TOK_KEYWORD_FUNCTION: |
||
13321 |
res = parse_function(p); |
||
13322 |
break; |
||
13323 |
case TOK_KEYWORD_THIS: |
||
13324 |
1 |
emit_byte(p, OP_PUSH_THIS); |
|
13325 |
1 |
break; |
|
13326 |
default: |
||
13327 |
SYNTAX_ERROR(p); |
||
13328 |
} |
||
13329 |
✓✗✗✓ |
153 |
if (tok != TOK_KEYWORD_FUNCTION) pnext1(p); |
13330 |
153 |
return res; |
|
13331 |
} |
||
13332 |
|||
13333 |
153 |
static mjs_err_t parse_call_dot_mem(struct pstate *p, int prev_op) { |
|
13334 |
153 |
int ops[] = {TOK_DOT, TOK_OPEN_PAREN, TOK_OPEN_BRACKET, TOK_EOF}; |
|
13335 |
153 |
mjs_err_t res = MJS_OK; |
|
13336 |
✗✓ | 153 |
if ((res = parse_literal(p, &p->tok)) != MJS_OK) return res; |
13337 |
✓✓ | 309 |
while (findtok(ops, p->tok.tok) != TOK_EOF) { |
13338 |
✓✓ | 3 |
if (p->tok.tok == TOK_OPEN_BRACKET) { |
13339 |
1 |
int prev_tok = p->prev_tok; |
|
13340 |
✗✓✗✓ |
1 |
EXPECT(p, TOK_OPEN_BRACKET); |
13341 |
✗✓ | 1 |
if ((res = parse_expr(p)) != MJS_OK) return res; |
13342 |
1 |
emit_byte(p, OP_SWAP); |
|
13343 |
✗✓✗✓ |
1 |
EXPECT(p, TOK_CLOSE_BRACKET); |
13344 |
✓✗✓✗ |
2 |
if (!findtok(s_assign_ops, p->tok.tok) && |
13345 |
✓✗ | 2 |
!findtok(s_postfix_ops, p->tok.tok) && |
13346 |
/* TODO(dfrank): fix: it doesn't work for prefix ops */ |
||
13347 |
1 |
!findtok(s_postfix_ops, prev_tok)) { |
|
13348 |
1 |
emit_byte(p, OP_GET); |
|
13349 |
} |
||
13350 |
✓✓ | 2 |
} else if (p->tok.tok == TOK_OPEN_PAREN) { |
13351 |
✗✓✗✓ |
1 |
EXPECT(p, TOK_OPEN_PAREN); |
13352 |
1 |
emit_byte(p, OP_ARGS); |
|
13353 |
✓✓ | 4 |
while (p->tok.tok != TOK_CLOSE_PAREN) { |
13354 |
✗✓ | 2 |
if ((res = parse_expr(p)) != MJS_OK) return res; |
13355 |
✗✓✗✗ |
2 |
if (p->tok.tok == TOK_COMMA) pnext1(p); |
13356 |
} |
||
13357 |
1 |
emit_byte(p, OP_CALL); |
|
13358 |
✗✓✗✓ |
1 |
EXPECT(p, TOK_CLOSE_PAREN); |
13359 |
✓✗ | 1 |
} else if (p->tok.tok == TOK_DOT) { |
13360 |
✗✓✗✓ |
1 |
EXPECT(p, TOK_DOT); |
13361 |
✗✓ | 1 |
if ((res = parse_call_dot_mem(p, TOK_DOT)) != MJS_OK) return res; |
13362 |
} |
||
13363 |
} |
||
13364 |
(void) prev_op; |
||
13365 |
153 |
return res; |
|
13366 |
} |
||
13367 |
|||
13368 |
152 |
static mjs_err_t parse_postfix(struct pstate *p, int prev_op) { |
|
13369 |
152 |
mjs_err_t res = MJS_OK; |
|
13370 |
✗✓ | 152 |
if ((res = parse_call_dot_mem(p, prev_op)) != MJS_OK) return res; |
13371 |
✓✓✓✓ |
152 |
if (p->tok.tok == TOK_PLUS_PLUS || p->tok.tok == TOK_MINUS_MINUS) { |
13372 |
✓✓ | 3 |
int op = p->tok.tok == TOK_PLUS_PLUS ? TOK_POSTFIX_PLUS : TOK_POSTFIX_MINUS; |
13373 |
3 |
emit_op(p, op); |
|
13374 |
✗✓ | 3 |
pnext1(p); |
13375 |
} |
||
13376 |
152 |
return res; |
|
13377 |
} |
||
13378 |
|||
13379 |
154 |
static mjs_err_t parse_unary(struct pstate *p, int prev_op) { |
|
13380 |
154 |
mjs_err_t res = MJS_OK; |
|
13381 |
154 |
int op = TOK_EOF; |
|
13382 |
✓✓ | 154 |
if (findtok(s_unary_ops, p->tok.tok) != TOK_EOF) { |
13383 |
16 |
op = p->tok.tok; |
|
13384 |
✗✓ | 16 |
pnext1(p); |
13385 |
} |
||
13386 |
✓✓ | 154 |
if (findtok(s_unary_ops, p->tok.tok) != TOK_EOF) { |
13387 |
2 |
res = parse_unary(p, prev_op); |
|
13388 |
} else { |
||
13389 |
152 |
res = parse_postfix(p, prev_op); |
|
13390 |
} |
||
13391 |
✗✓ | 154 |
if (res != MJS_OK) return res; |
13392 |
✓✓ | 154 |
if (op != TOK_EOF) { |
13393 |
✓✓ | 16 |
if (op == TOK_MINUS) op = TOK_UNARY_MINUS; |
13394 |
✓✓ | 16 |
if (op == TOK_PLUS) op = TOK_UNARY_PLUS; |
13395 |
16 |
emit_op(p, op); |
|
13396 |
} |
||
13397 |
154 |
return res; |
|
13398 |
} |
||
13399 |
|||
13400 |
152 |
static mjs_err_t parse_mul_div_rem(struct pstate *p, int prev_op) { |
|
13401 |
152 |
int ops[] = {TOK_MUL, TOK_DIV, TOK_REM, TOK_EOF}; |
|
13402 |
✗✓✗✓ ✓✓✓✓ ✓✗✗✓ ✗✗✗✓ ✗✓✗✓ |
152 |
PARSE_LTR_BINOP(p, parse_unary, parse_mul_div_rem, ops, prev_op); |
13403 |
} |
||
13404 |
|||
13405 |
141 |
static mjs_err_t parse_plus_minus(struct pstate *p, int prev_op) { |
|
13406 |
141 |
int ops[] = {TOK_PLUS, TOK_MINUS, TOK_EOF}; |
|
13407 |
✗✓✗✓ ✓✓✓✓ ✓✗✗✓ ✗✗✗✓ ✗✓✗✓ |
141 |
PARSE_LTR_BINOP(p, parse_mul_div_rem, parse_plus_minus, ops, prev_op); |
13408 |
} |
||
13409 |
|||
13410 |
137 |
static mjs_err_t parse_shifts(struct pstate *p, int prev_op) { |
|
13411 |
137 |
int ops[] = {TOK_LSHIFT, TOK_RSHIFT, TOK_URSHIFT, TOK_EOF}; |
|
13412 |
✗✓✗✓ ✓✓✓✓ ✓✗✗✓ ✗✗✗✓ ✗✓✗✓ |
137 |
PARSE_LTR_BINOP(p, parse_plus_minus, parse_shifts, ops, prev_op); |
13413 |
} |
||
13414 |
|||
13415 |
135 |
static mjs_err_t parse_comparison(struct pstate *p, int prev_op) { |
|
13416 |
✗✓✗✓ ✓✓✓✓ ✓✗✗✓ ✗✗✗✓ ✗✓✗✓ |
135 |
PARSE_LTR_BINOP(p, parse_shifts, parse_comparison, s_comparison_ops, prev_op); |
13417 |
} |
||
13418 |
|||
13419 |
129 |
static mjs_err_t parse_equality(struct pstate *p, int prev_op) { |
|
13420 |
✗✓✗✓ ✓✓✓✓ ✓✗✗✓ ✗✗✗✓ ✗✓✗✓ |
129 |
PARSE_LTR_BINOP(p, parse_comparison, parse_equality, s_equality_ops, prev_op); |
13421 |
} |
||
13422 |
|||
13423 |
127 |
static mjs_err_t parse_bitwise_and(struct pstate *p, int prev_op) { |
|
13424 |
127 |
int ops[] = {TOK_AND, TOK_EOF}; |
|
13425 |
✗✓✗✓ ✓✓✓✓ ✓✗✗✓ ✗✗✗✓ ✗✓✗✓ |
127 |
PARSE_LTR_BINOP(p, parse_equality, parse_bitwise_and, ops, prev_op); |
13426 |
} |
||
13427 |
|||
13428 |
123 |
static mjs_err_t parse_bitwise_xor(struct pstate *p, int prev_op) { |
|
13429 |
123 |
int ops[] = {TOK_XOR, TOK_EOF}; |
|
13430 |
✗✓✗✓ ✓✓✓✓ ✓✗✗✓ ✗✗✗✓ ✗✓✗✓ |
123 |
PARSE_LTR_BINOP(p, parse_bitwise_and, parse_bitwise_xor, ops, prev_op); |
13431 |
} |
||
13432 |
|||
13433 |
119 |
static mjs_err_t parse_bitwise_or(struct pstate *p, int prev_op) { |
|
13434 |
119 |
int ops[] = {TOK_OR, TOK_EOF}; |
|
13435 |
✗✓✗✓ ✓✓✓✓ ✓✗✗✓ ✗✗✗✓ ✗✓✗✓ |
119 |
PARSE_LTR_BINOP(p, parse_bitwise_xor, parse_bitwise_or, ops, prev_op); |
13436 |
} |
||
13437 |
|||
13438 |
113 |
static mjs_err_t parse_logical_and(struct pstate *p, int prev_op) { |
|
13439 |
113 |
int ops[] = {TOK_LOGICAL_AND, TOK_EOF}; |
|
13440 |
✗✓✗✓ ✗✓✓✓ ✗✓✗✗ ✓✗✗✓ ✗✓✓✗ |
113 |
PARSE_LTR_BINOP(p, parse_bitwise_or, parse_logical_and, ops, prev_op); |
13441 |
} |
||
13442 |
|||
13443 |
112 |
static mjs_err_t parse_logical_or(struct pstate *p, int prev_op) { |
|
13444 |
112 |
int ops[] = {TOK_LOGICAL_OR, TOK_EOF}; |
|
13445 |
✗✓✗✓ ✗✓✗✓ ✗✗✗✗ ✗✗✗✗ ✗✗✗✗ |
112 |
PARSE_LTR_BINOP(p, parse_logical_and, parse_logical_or, ops, prev_op); |
13446 |
} |
||
13447 |
|||
13448 |
112 |
static mjs_err_t parse_ternary(struct pstate *p, int prev_op) { |
|
13449 |
112 |
mjs_err_t res = MJS_OK; |
|
13450 |
✗✓ | 112 |
if ((res = parse_logical_or(p, TOK_EOF)) != MJS_OK) return res; |
13451 |
✗✓ | 112 |
if (prev_op != TOK_EOF) emit_op(p, prev_op); |
13452 |
|||
13453 |
✓✓ | 112 |
if (p->tok.tok == TOK_QUESTION) { |
13454 |
size_t off_if, off_endif, off_else; |
||
13455 |
✗✓✗✓ |
2 |
EXPECT(p, TOK_QUESTION); |
13456 |
|||
13457 |
2 |
emit_byte(p, OP_JMP_FALSE); |
|
13458 |
2 |
off_if = p->cur_idx; |
|
13459 |
2 |
emit_init_offset(p); |
|
13460 |
|||
13461 |
✗✓ | 2 |
if ((res = parse_ternary(p, TOK_EOF)) != MJS_OK) return res; |
13462 |
|||
13463 |
2 |
emit_byte(p, OP_JMP); |
|
13464 |
2 |
off_else = p->cur_idx; |
|
13465 |
2 |
emit_init_offset(p); |
|
13466 |
2 |
off_endif = p->cur_idx; |
|
13467 |
|||
13468 |
2 |
emit_byte(p, OP_DROP); |
|
13469 |
|||
13470 |
✗✓✗✓ |
2 |
EXPECT(p, TOK_COLON); |
13471 |
✗✓ | 2 |
if ((res = parse_ternary(p, TOK_EOF)) != MJS_OK) return res; |
13472 |
|||
13473 |
/* |
||
13474 |
* NOTE: if inserting offset causes the code to move, off_endif needs to be |
||
13475 |
* adjusted |
||
13476 |
*/ |
||
13477 |
2 |
off_endif += mjs_bcode_insert_offset( |
|
13478 |
2 |
p, p->mjs, off_else, p->cur_idx - off_else - MJS_INIT_OFFSET_SIZE); |
|
13479 |
|||
13480 |
2 |
mjs_bcode_insert_offset(p, p->mjs, off_if, |
|
13481 |
2 |
off_endif - off_if - MJS_INIT_OFFSET_SIZE); |
|
13482 |
} |
||
13483 |
|||
13484 |
112 |
return res; |
|
13485 |
} |
||
13486 |
|||
13487 |
108 |
static mjs_err_t parse_assignment(struct pstate *p, int prev_op) { |
|
13488 |
✗✓✓✓ ✗✓✗✓ |
108 |
PARSE_RTL_BINOP(p, parse_ternary, parse_assignment, s_assign_ops, prev_op); |
13489 |
} |
||
13490 |
|||
13491 |
98 |
static mjs_err_t parse_expr(struct pstate *p) { |
|
13492 |
98 |
return parse_assignment(p, TOK_EOF); |
|
13493 |
} |
||
13494 |
|||
13495 |
1 |
static mjs_err_t parse_let(struct pstate *p) { |
|
13496 |
1 |
mjs_err_t res = MJS_OK; |
|
13497 |
✗✓ | 1 |
LOG(LL_VERBOSE_DEBUG, ("[%.*s]", 10, p->tok.ptr)); |
13498 |
✗✓✗✓ |
1 |
EXPECT(p, TOK_KEYWORD_LET); |
13499 |
for (;;) { |
||
13500 |
1 |
struct tok tmp = p->tok; |
|
13501 |
✗✓✗✓ |
1 |
EXPECT(p, TOK_IDENT); |
13502 |
|||
13503 |
1 |
emit_byte(p, OP_PUSH_STR); |
|
13504 |
1 |
emit_str(p, tmp.ptr, tmp.len); |
|
13505 |
1 |
emit_byte(p, OP_PUSH_SCOPE); |
|
13506 |
1 |
emit_byte(p, OP_CREATE); |
|
13507 |
|||
13508 |
✗✓ | 1 |
if (p->tok.tok == TOK_ASSIGN) { |
13509 |
pnext1(p); |
||
13510 |
emit_byte(p, OP_PUSH_STR); |
||
13511 |
emit_str(p, tmp.ptr, tmp.len); |
||
13512 |
emit_byte(p, OP_FIND_SCOPE); |
||
13513 |
if ((res = parse_expr(p)) != MJS_OK) return res; |
||
13514 |
emit_op(p, TOK_ASSIGN); |
||
13515 |
} else { |
||
13516 |
1 |
emit_byte(p, OP_PUSH_UNDEF); |
|
13517 |
} |
||
13518 |
✗✓ | 1 |
if (p->tok.tok == TOK_COMMA) { |
13519 |
emit_byte(p, OP_DROP); |
||
13520 |
pnext1(p); |
||
13521 |
} |
||
13522 |
✓✗✗✓ |
1 |
if (p->tok.tok == TOK_SEMICOLON || p->tok.tok == TOK_EOF) break; |
13523 |
} |
||
13524 |
1 |
return res; |
|
13525 |
} |
||
13526 |
|||
13527 |
5 |
static mjs_err_t parse_block_or_stmt(struct pstate *p, int cs) { |
|
13528 |
✓✓ | 5 |
if (ptest(p) == TOK_OPEN_CURLY) { |
13529 |
1 |
return parse_block(p, cs); |
|
13530 |
} else { |
||
13531 |
4 |
return parse_statement(p); |
|
13532 |
} |
||
13533 |
} |
||
13534 |
|||
13535 |
static mjs_err_t parse_for_in(struct pstate *p) { |
||
13536 |
mjs_err_t res = MJS_OK; |
||
13537 |
size_t off_b, off_check_end; |
||
13538 |
|||
13539 |
/* new scope should be pushed before OP_LOOP instruction */ |
||
13540 |
emit_byte(p, OP_NEW_SCOPE); |
||
13541 |
|||
13542 |
/* Put iterator variable name to the stack */ |
||
13543 |
if (p->tok.tok == TOK_KEYWORD_LET) { |
||
13544 |
EXPECT(p, TOK_KEYWORD_LET); |
||
13545 |
emit_byte(p, OP_PUSH_STR); |
||
13546 |
emit_str(p, p->tok.ptr, p->tok.len); |
||
13547 |
emit_byte(p, OP_PUSH_SCOPE); |
||
13548 |
emit_byte(p, OP_CREATE); |
||
13549 |
} |
||
13550 |
emit_byte(p, OP_PUSH_STR); |
||
13551 |
emit_str(p, p->tok.ptr, p->tok.len); |
||
13552 |
|||
13553 |
/* Put object to the stack */ |
||
13554 |
EXPECT(p, TOK_IDENT); |
||
13555 |
EXPECT(p, TOK_KEYWORD_IN); |
||
13556 |
parse_expr(p); |
||
13557 |
EXPECT(p, TOK_CLOSE_PAREN); |
||
13558 |
|||
13559 |
emit_byte(p, OP_PUSH_UNDEF); /* Push iterator */ |
||
13560 |
|||
13561 |
/* Before parsing condition statement, push break/continue offsets */ |
||
13562 |
emit_byte(p, OP_LOOP); |
||
13563 |
off_b = p->cur_idx; |
||
13564 |
emit_init_offset(p); |
||
13565 |
emit_byte(p, 0); /* Point OP_CONTINUE to the next instruction */ |
||
13566 |
|||
13567 |
emit_byte(p, OP_FOR_IN_NEXT); |
||
13568 |
emit_byte(p, OP_DUP); |
||
13569 |
emit_byte(p, OP_JMP_FALSE); |
||
13570 |
off_check_end = p->cur_idx; |
||
13571 |
emit_init_offset(p); |
||
13572 |
|||
13573 |
// Parse loop body |
||
13574 |
if (p->tok.tok == TOK_OPEN_CURLY) { |
||
13575 |
if ((res = parse_statement_list(p, TOK_CLOSE_CURLY)) != MJS_OK) return res; |
||
13576 |
pnext1(p); |
||
13577 |
} else { |
||
13578 |
if ((res = parse_statement(p)) != MJS_OK) return res; |
||
13579 |
} |
||
13580 |
emit_byte(p, OP_DROP); |
||
13581 |
emit_byte(p, OP_CONTINUE); |
||
13582 |
|||
13583 |
/* jump cond -> break */ |
||
13584 |
mjs_bcode_insert_offset(p, p->mjs, off_check_end, |
||
13585 |
p->cur_idx - off_check_end - MJS_INIT_OFFSET_SIZE); |
||
13586 |
|||
13587 |
/* NOTE: jump C -> cond link is already established, it's constant: zero */ |
||
13588 |
|||
13589 |
emit_byte(p, OP_BREAK); |
||
13590 |
|||
13591 |
/* jump B -> cond */ |
||
13592 |
mjs_bcode_insert_offset(p, p->mjs, off_b, |
||
13593 |
p->cur_idx - off_b - MJS_INIT_OFFSET_SIZE); |
||
13594 |
|||
13595 |
emit_byte(p, OP_DROP); |
||
13596 |
emit_byte(p, OP_DROP); |
||
13597 |
emit_byte(p, OP_DROP); |
||
13598 |
emit_byte(p, OP_DEL_SCOPE); |
||
13599 |
|||
13600 |
return res; |
||
13601 |
} |
||
13602 |
|||
13603 |
static int check_for_in(struct pstate *p) { |
||
13604 |
struct pstate saved = *p; |
||
13605 |
int forin = 0; |
||
13606 |
if (p->tok.tok == TOK_KEYWORD_LET) pnext1(p); |
||
13607 |
if (p->tok.tok == TOK_IDENT) { |
||
13608 |
pnext1(p); |
||
13609 |
if (p->tok.tok == TOK_KEYWORD_IN) forin = 1; |
||
13610 |
} |
||
13611 |
*p = saved; |
||
13612 |
return forin; |
||
13613 |
} |
||
13614 |
|||
13615 |
static mjs_err_t parse_for(struct pstate *p) { |
||
13616 |
mjs_err_t res = MJS_OK; |
||
13617 |
size_t off_b, off_c, off_init_end; |
||
13618 |
size_t off_incr_begin, off_cond_begin, off_cond_end; |
||
13619 |
int buf_cur_idx; |
||
13620 |
|||
13621 |
LOG(LL_VERBOSE_DEBUG, ("[%.*s]", 10, p->tok.ptr)); |
||
13622 |
EXPECT(p, TOK_KEYWORD_FOR); |
||
13623 |
EXPECT(p, TOK_OPEN_PAREN); |
||
13624 |
|||
13625 |
/* Look forward - is it for..in ? */ |
||
13626 |
if (check_for_in(p)) return parse_for_in(p); |
||
13627 |
|||
13628 |
/* |
||
13629 |
* BC is a break+continue offsets (a part of OP_LOOP opcode) |
||
13630 |
* |
||
13631 |
* BC init incr cond body break del_scope |
||
13632 |
* || | ^ ^ | ^ ^ |
||
13633 |
* || +--|-----+ | | | |
||
13634 |
* |+-------+ +--------+ | |
||
13635 |
* +---------------------------------+ |
||
13636 |
* |
||
13637 |
* The order to setup links: |
||
13638 |
* |
||
13639 |
* cond -> break |
||
13640 |
* init -> cond |
||
13641 |
* C -> incr |
||
13642 |
* B -> del_scope |
||
13643 |
*/ |
||
13644 |
|||
13645 |
/* new scope should be pushed before OP_LOOP instruction */ |
||
13646 |
emit_byte(p, OP_NEW_SCOPE); |
||
13647 |
|||
13648 |
/* Before parsing condition statement, push break/continue offsets */ |
||
13649 |
emit_byte(p, OP_LOOP); |
||
13650 |
off_b = p->cur_idx; |
||
13651 |
emit_init_offset(p); |
||
13652 |
off_c = p->cur_idx; |
||
13653 |
emit_init_offset(p); |
||
13654 |
|||
13655 |
/* Parse init statement */ |
||
13656 |
if (p->tok.tok == TOK_KEYWORD_LET) { |
||
13657 |
if ((res = parse_let(p)) != MJS_OK) return res; |
||
13658 |
} else { |
||
13659 |
if ((res = parse_expr(p)) != MJS_OK) return res; |
||
13660 |
} |
||
13661 |
EXPECT(p, TOK_SEMICOLON); |
||
13662 |
emit_byte(p, OP_DROP); |
||
13663 |
|||
13664 |
emit_byte(p, OP_JMP); |
||
13665 |
off_init_end = p->cur_idx; |
||
13666 |
emit_init_offset(p); |
||
13667 |
|||
13668 |
off_incr_begin = p->cur_idx; |
||
13669 |
off_cond_begin = p->cur_idx; |
||
13670 |
|||
13671 |
/* Parse cond statement */ |
||
13672 |
if ((res = parse_expr(p)) != MJS_OK) return res; |
||
13673 |
EXPECT(p, TOK_SEMICOLON); |
||
13674 |
|||
13675 |
/* Parse incr statement */ |
||
13676 |
/* Incr statement should be placed before cond, so, adjust cur_idx */ |
||
13677 |
buf_cur_idx = p->cur_idx; |
||
13678 |
p->cur_idx = off_incr_begin; |
||
13679 |
|||
13680 |
if ((res = parse_expr(p)) != MJS_OK) return res; |
||
13681 |
EXPECT(p, TOK_CLOSE_PAREN); |
||
13682 |
emit_byte(p, OP_DROP); |
||
13683 |
|||
13684 |
/* |
||
13685 |
* Now incr is inserted before cond, so we adjust cur_idx back, and set |
||
13686 |
* off_cond_begin to the correct value |
||
13687 |
*/ |
||
13688 |
{ |
||
13689 |
int incr_size = p->cur_idx - off_incr_begin; |
||
13690 |
off_cond_begin += incr_size; |
||
13691 |
p->cur_idx = buf_cur_idx + incr_size; |
||
13692 |
} |
||
13693 |
|||
13694 |
/* p->cur_idx is now at the end of "cond" */ |
||
13695 |
/* Exit the loop if false */ |
||
13696 |
emit_byte(p, OP_JMP_FALSE); |
||
13697 |
off_cond_end = p->cur_idx; |
||
13698 |
emit_init_offset(p); |
||
13699 |
|||
13700 |
/* Parse loop body */ |
||
13701 |
if (p->tok.tok == TOK_OPEN_CURLY) { |
||
13702 |
if ((res = parse_statement_list(p, TOK_CLOSE_CURLY)) != MJS_OK) return res; |
||
13703 |
pnext1(p); |
||
13704 |
} else { |
||
13705 |
if ((res = parse_statement(p)) != MJS_OK) return res; |
||
13706 |
} |
||
13707 |
emit_byte(p, OP_DROP); |
||
13708 |
emit_byte(p, OP_CONTINUE); |
||
13709 |
|||
13710 |
/* p->cur_idx is at the "break" item now */ |
||
13711 |
|||
13712 |
/* jump cond -> break */ |
||
13713 |
mjs_bcode_insert_offset(p, p->mjs, off_cond_end, |
||
13714 |
p->cur_idx - off_cond_end - MJS_INIT_OFFSET_SIZE); |
||
13715 |
|||
13716 |
/* jump init -> cond (and adjust off_incr_begin which may move) */ |
||
13717 |
off_incr_begin += mjs_bcode_insert_offset( |
||
13718 |
p, p->mjs, off_init_end, |
||
13719 |
off_cond_begin - off_init_end - MJS_INIT_OFFSET_SIZE); |
||
13720 |
|||
13721 |
/* jump C -> incr */ |
||
13722 |
mjs_bcode_insert_offset(p, p->mjs, off_c, |
||
13723 |
off_incr_begin - off_c - MJS_INIT_OFFSET_SIZE); |
||
13724 |
|||
13725 |
emit_byte(p, OP_BREAK); |
||
13726 |
|||
13727 |
/* jump B -> del_scope */ |
||
13728 |
mjs_bcode_insert_offset(p, p->mjs, off_b, |
||
13729 |
p->cur_idx - off_b - MJS_INIT_OFFSET_SIZE); |
||
13730 |
|||
13731 |
emit_byte(p, OP_DEL_SCOPE); |
||
13732 |
|||
13733 |
return res; |
||
13734 |
} |
||
13735 |
|||
13736 |
static mjs_err_t parse_while(struct pstate *p) { |
||
13737 |
size_t off_cond_end, off_b; |
||
13738 |
mjs_err_t res = MJS_OK; |
||
13739 |
|||
13740 |
EXPECT(p, TOK_KEYWORD_WHILE); |
||
13741 |
EXPECT(p, TOK_OPEN_PAREN); |
||
13742 |
|||
13743 |
/* new scope should be pushed before OP_LOOP instruction */ |
||
13744 |
emit_byte(p, OP_NEW_SCOPE); |
||
13745 |
|||
13746 |
/* |
||
13747 |
* BC is a break+continue offsets (a part of OP_LOOP opcode) |
||
13748 |
* |
||
13749 |
* BC cond body break del_scope |
||
13750 |
* || ^ | ^ ^ |
||
13751 |
* || | | | | |
||
13752 |
* |+-+ +------+ | |
||
13753 |
* +------------------+ |
||
13754 |
* |
||
13755 |
* The order to setup links: |
||
13756 |
* |
||
13757 |
* cond -> break |
||
13758 |
* C -> cond |
||
13759 |
* B -> del_scope |
||
13760 |
*/ |
||
13761 |
|||
13762 |
emit_byte(p, OP_LOOP); |
||
13763 |
off_b = p->cur_idx; |
||
13764 |
emit_init_offset(p); |
||
13765 |
emit_byte(p, 0); /* Point OP_CONTINUE to the next instruction */ |
||
13766 |
|||
13767 |
// parse condition statement |
||
13768 |
if ((res = parse_expr(p)) != MJS_OK) return res; |
||
13769 |
EXPECT(p, TOK_CLOSE_PAREN); |
||
13770 |
|||
13771 |
// Exit the loop if false |
||
13772 |
emit_byte(p, OP_JMP_FALSE); |
||
13773 |
off_cond_end = p->cur_idx; |
||
13774 |
emit_init_offset(p); |
||
13775 |
|||
13776 |
// Parse loop body |
||
13777 |
if (p->tok.tok == TOK_OPEN_CURLY) { |
||
13778 |
if ((res = parse_statement_list(p, TOK_CLOSE_CURLY)) != MJS_OK) return res; |
||
13779 |
pnext1(p); |
||
13780 |
} else { |
||
13781 |
if ((res = parse_statement(p)) != MJS_OK) return res; |
||
13782 |
} |
||
13783 |
emit_byte(p, OP_DROP); |
||
13784 |
emit_byte(p, OP_CONTINUE); |
||
13785 |
|||
13786 |
/* jump cond -> break */ |
||
13787 |
mjs_bcode_insert_offset(p, p->mjs, off_cond_end, |
||
13788 |
p->cur_idx - off_cond_end - MJS_INIT_OFFSET_SIZE); |
||
13789 |
|||
13790 |
/* NOTE: jump C -> cond link is already established, it's constant: zero */ |
||
13791 |
|||
13792 |
emit_byte(p, OP_BREAK); |
||
13793 |
|||
13794 |
/* jump B -> cond */ |
||
13795 |
mjs_bcode_insert_offset(p, p->mjs, off_b, |
||
13796 |
p->cur_idx - off_b - MJS_INIT_OFFSET_SIZE); |
||
13797 |
|||
13798 |
emit_byte(p, OP_DEL_SCOPE); |
||
13799 |
return res; |
||
13800 |
} |
||
13801 |
|||
13802 |
5 |
static mjs_err_t parse_if(struct pstate *p) { |
|
13803 |
size_t off_if, off_endif; |
||
13804 |
5 |
mjs_err_t res = MJS_OK; |
|
13805 |
✗✓ | 5 |
LOG(LL_VERBOSE_DEBUG, ("[%.*s]", 10, p->tok.ptr)); |
13806 |
✗✓✗✓ |
5 |
EXPECT(p, TOK_KEYWORD_IF); |
13807 |
✗✓✗✓ |
5 |
EXPECT(p, TOK_OPEN_PAREN); |
13808 |
✗✓ | 5 |
if ((res = parse_expr(p)) != MJS_OK) return res; |
13809 |
|||
13810 |
5 |
emit_byte(p, OP_JMP_FALSE); |
|
13811 |
5 |
off_if = p->cur_idx; |
|
13812 |
5 |
emit_init_offset(p); |
|
13813 |
|||
13814 |
✗✓✗✓ |
5 |
EXPECT(p, TOK_CLOSE_PAREN); |
13815 |
✗✓ | 5 |
if ((res = parse_block_or_stmt(p, 1)) != MJS_OK) return res; |
13816 |
|||
13817 |
✗✓ | 5 |
if (p->tok.tok == TOK_KEYWORD_ELSE) { |
13818 |
/* |
||
13819 |
* Else clause is present, so, if the condition is not true, the jump |
||
13820 |
* target (off_endif) should be not the current offset, but the offset |
||
13821 |
* after jump-over-else opcode |
||
13822 |
*/ |
||
13823 |
size_t off_else, off_endelse; |
||
13824 |
pnext1(p); |
||
13825 |
emit_byte(p, OP_JMP); |
||
13826 |
off_else = p->cur_idx; |
||
13827 |
emit_init_offset(p); |
||
13828 |
off_endif = p->cur_idx; |
||
13829 |
|||
13830 |
emit_byte(p, OP_DROP); |
||
13831 |
if ((res = parse_block_or_stmt(p, 1)) != MJS_OK) return res; |
||
13832 |
off_endelse = p->cur_idx; |
||
13833 |
|||
13834 |
/* |
||
13835 |
* NOTE: if inserting offset causes the code to move, off_endif needs to be |
||
13836 |
* adjusted |
||
13837 |
*/ |
||
13838 |
off_endif += mjs_bcode_insert_offset( |
||
13839 |
p, p->mjs, off_else, off_endelse - off_else - MJS_INIT_OFFSET_SIZE); |
||
13840 |
} else { |
||
13841 |
/* Else clause is not present, so, current offset is a jump target |
||
13842 |
* (off_endif) */ |
||
13843 |
5 |
off_endif = p->cur_idx; |
|
13844 |
} |
||
13845 |
|||
13846 |
5 |
mjs_bcode_insert_offset(p, p->mjs, off_if, |
|
13847 |
5 |
off_endif - off_if - MJS_INIT_OFFSET_SIZE); |
|
13848 |
|||
13849 |
5 |
return res; |
|
13850 |
} |
||
13851 |
|||
13852 |
static void pstate_revert(struct pstate *p, struct pstate *old, |
||
13853 |
int old_bcode_gen_len) { |
||
13854 |
p->pos = old->pos; |
||
13855 |
p->line_no = old->line_no; |
||
13856 |
p->last_emitted_line_no = old->last_emitted_line_no; |
||
13857 |
p->offset_lineno_map.len = old->offset_lineno_map.len; |
||
13858 |
p->prev_tok = old->prev_tok; |
||
13859 |
p->tok = old->tok; |
||
13860 |
p->mjs->bcode_gen.len = old_bcode_gen_len; |
||
13861 |
p->cur_idx = old->cur_idx; |
||
13862 |
p->depth = old->depth; |
||
13863 |
} |
||
13864 |
|||
13865 |
2 |
static mjs_err_t parse_return(struct pstate *p) { |
|
13866 |
int old_bcode_gen_len; |
||
13867 |
struct pstate p_saved; |
||
13868 |
✗✓✗✓ |
2 |
EXPECT(p, TOK_KEYWORD_RETURN); |
13869 |
2 |
p_saved = *p; |
|
13870 |
2 |
old_bcode_gen_len = p->mjs->bcode_gen.len; |
|
13871 |
✗✓ | 2 |
if (parse_expr(p) != MJS_OK) { |
13872 |
/* |
||
13873 |
* Failed to parse an expression to return, so return the parser to the |
||
13874 |
* prior state and push undefined. |
||
13875 |
*/ |
||
13876 |
pstate_revert(p, &p_saved, old_bcode_gen_len); |
||
13877 |
emit_byte(p, OP_PUSH_UNDEF); |
||
13878 |
} |
||
13879 |
2 |
emit_byte(p, OP_SETRETVAL); |
|
13880 |
2 |
emit_byte(p, OP_RETURN); |
|
13881 |
2 |
return MJS_OK; |
|
13882 |
} |
||
13883 |
|||
13884 |
110 |
static mjs_err_t parse_statement(struct pstate *p) { |
|
13885 |
✗✓ | 110 |
LOG(LL_VERBOSE_DEBUG, ("[%.*s]", 10, p->tok.ptr)); |
13886 |
✓✓✓✓ ✗✗✓✓ ✓✓✓ |
110 |
switch (p->tok.tok) { |
13887 |
case TOK_SEMICOLON: |
||
13888 |
1 |
emit_byte(p, OP_PUSH_UNDEF); |
|
13889 |
✗✓ | 1 |
pnext1(p); |
13890 |
1 |
return MJS_OK; |
|
13891 |
case TOK_KEYWORD_LET: |
||
13892 |
1 |
return parse_let(p); |
|
13893 |
case TOK_OPEN_CURLY: |
||
13894 |
4 |
return parse_block(p, 1); |
|
13895 |
case TOK_KEYWORD_RETURN: |
||
13896 |
2 |
return parse_return(p); |
|
13897 |
case TOK_KEYWORD_FOR: |
||
13898 |
return parse_for(p); |
||
13899 |
case TOK_KEYWORD_WHILE: |
||
13900 |
return parse_while(p); |
||
13901 |
case TOK_KEYWORD_BREAK: |
||
13902 |
1 |
emit_byte(p, OP_PUSH_UNDEF); |
|
13903 |
1 |
emit_byte(p, OP_BREAK); |
|
13904 |
✗✓ | 1 |
pnext1(p); |
13905 |
1 |
return MJS_OK; |
|
13906 |
case TOK_KEYWORD_CONTINUE: |
||
13907 |
1 |
emit_byte(p, OP_CONTINUE); |
|
13908 |
✗✓ | 1 |
pnext1(p); |
13909 |
1 |
return MJS_OK; |
|
13910 |
case TOK_KEYWORD_IF: |
||
13911 |
5 |
return parse_if(p); |
|
13912 |
case TOK_KEYWORD_CASE: |
||
13913 |
case TOK_KEYWORD_CATCH: |
||
13914 |
case TOK_KEYWORD_DELETE: |
||
13915 |
case TOK_KEYWORD_DO: |
||
13916 |
case TOK_KEYWORD_INSTANCEOF: |
||
13917 |
case TOK_KEYWORD_NEW: |
||
13918 |
case TOK_KEYWORD_SWITCH: |
||
13919 |
case TOK_KEYWORD_THROW: |
||
13920 |
case TOK_KEYWORD_TRY: |
||
13921 |
case TOK_KEYWORD_VAR: |
||
13922 |
case TOK_KEYWORD_VOID: |
||
13923 |
case TOK_KEYWORD_WITH: |
||
13924 |
12 |
mjs_set_errorf(p->mjs, MJS_SYNTAX_ERROR, "[%.*s] is not implemented", |
|
13925 |
p->tok.len, p->tok.ptr); |
||
13926 |
12 |
return MJS_SYNTAX_ERROR; |
|
13927 |
default: { |
||
13928 |
83 |
mjs_err_t res = MJS_OK; |
|
13929 |
for (;;) { |
||
13930 |
✗✓ | 84 |
if ((res = parse_expr(p)) != MJS_OK) return res; |
13931 |
✓✓ | 84 |
if (p->tok.tok != TOK_COMMA) break; |
13932 |
1 |
emit_byte(p, OP_DROP); |
|
13933 |
✗✓ | 1 |
pnext1(p); |
13934 |
1 |
} |
|
13935 |
83 |
return res; |
|
13936 |
} |
||
13937 |
} |
||
13938 |
} |
||
13939 |
|||
13940 |
MJS_PRIVATE mjs_err_t |
||
13941 |
78 |
mjs_parse(const char *path, const char *buf, struct mjs *mjs) { |
|
13942 |
78 |
mjs_err_t res = MJS_OK; |
|
13943 |
struct pstate p; |
||
13944 |
size_t start_idx, llen; |
||
13945 |
int map_len; |
||
13946 |
mjs_header_item_t bcode_offset, map_offset, total_size; |
||
13947 |
|||
13948 |
78 |
pinit(path, buf, &p); |
|
13949 |
78 |
p.mjs = mjs; |
|
13950 |
78 |
p.cur_idx = p.mjs->bcode_gen.len; |
|
13951 |
78 |
emit_byte(&p, OP_BCODE_HEADER); |
|
13952 |
|||
13953 |
/* |
||
13954 |
* TODO(dfrank): don't access mjs->bcode_gen directly, use emit_... API which |
||
13955 |
* takes care of p->cur_idx |
||
13956 |
*/ |
||
13957 |
|||
13958 |
/* Remember starting bcode position, and reserve the room for bcode header */ |
||
13959 |
78 |
start_idx = p.mjs->bcode_gen.len; |
|
13960 |
78 |
mbuf_append(&p.mjs->bcode_gen, NULL, |
|
13961 |
sizeof(mjs_header_item_t) * MJS_HDR_ITEMS_CNT); |
||
13962 |
|||
13963 |
/* Append NULL-terminated filename */ |
||
13964 |
78 |
mbuf_append(&p.mjs->bcode_gen, path, strlen(path) + 1 /* null-terminate */); |
|
13965 |
|||
13966 |
78 |
bcode_offset = p.mjs->bcode_gen.len - start_idx; |
|
13967 |
78 |
memcpy(p.mjs->bcode_gen.buf + start_idx + |
|
13968 |
sizeof(mjs_header_item_t) * MJS_HDR_ITEM_BCODE_OFFSET, |
||
13969 |
&bcode_offset, sizeof(mjs_header_item_t)); |
||
13970 |
|||
13971 |
78 |
p.start_bcode_idx = p.mjs->bcode_gen.len; |
|
13972 |
78 |
p.cur_idx = p.mjs->bcode_gen.len; |
|
13973 |
|||
13974 |
78 |
res = parse_statement_list(&p, TOK_EOF); |
|
13975 |
78 |
emit_byte(&p, OP_EXIT); |
|
13976 |
|||
13977 |
/* remember map offset */ |
||
13978 |
78 |
map_offset = p.mjs->bcode_gen.len - start_idx; |
|
13979 |
78 |
memcpy(p.mjs->bcode_gen.buf + start_idx + |
|
13980 |
sizeof(mjs_header_item_t) * MJS_HDR_ITEM_MAP_OFFSET, |
||
13981 |
&map_offset, sizeof(mjs_header_item_t)); |
||
13982 |
|||
13983 |
/* put map length varint */ |
||
13984 |
78 |
map_len = p.offset_lineno_map.len; |
|
13985 |
78 |
llen = cs_varint_llen(map_len); |
|
13986 |
78 |
mbuf_resize(&p.mjs->bcode_gen, p.mjs->bcode_gen.size + llen); |
|
13987 |
78 |
cs_varint_encode( |
|
13988 |
78 |
map_len, (uint8_t *) p.mjs->bcode_gen.buf + p.mjs->bcode_gen.len, llen); |
|
13989 |
78 |
p.mjs->bcode_gen.len += llen; |
|
13990 |
|||
13991 |
/* put the map itself */ |
||
13992 |
78 |
mbuf_append(&p.mjs->bcode_gen, p.offset_lineno_map.buf, |
|
13993 |
p.offset_lineno_map.len); |
||
13994 |
|||
13995 |
78 |
total_size = p.mjs->bcode_gen.len - start_idx; |
|
13996 |
78 |
memcpy(p.mjs->bcode_gen.buf + start_idx + |
|
13997 |
sizeof(mjs_header_item_t) * MJS_HDR_ITEM_TOTAL_SIZE, |
||
13998 |
&total_size, sizeof(mjs_header_item_t)); |
||
13999 |
|||
14000 |
78 |
mbuf_free(&p.offset_lineno_map); |
|
14001 |
|||
14002 |
/* |
||
14003 |
* If parsing was successful, commit the bcode; otherwise drop generated |
||
14004 |
* bcode |
||
14005 |
*/ |
||
14006 |
✓✓ | 78 |
if (res == MJS_OK) { |
14007 |
66 |
mjs_bcode_commit(mjs); |
|
14008 |
} else { |
||
14009 |
12 |
mbuf_free(&mjs->bcode_gen); |
|
14010 |
} |
||
14011 |
|||
14012 |
78 |
return res; |
|
14013 |
} |
||
14014 |
#ifdef MJS_MODULE_LINES |
||
14015 |
#line 1 "mjs/src/mjs_primitive.c" |
||
14016 |
#endif |
||
14017 |
/* |
||
14018 |
* Copyright (c) 2017 Cesanta Software Limited |
||
14019 |
* All rights reserved |
||
14020 |
*/ |
||
14021 |
|||
14022 |
/* Amalgamated: #include "mjs/src/mjs_core.h" */ |
||
14023 |
/* Amalgamated: #include "mjs/src/mjs_internal.h" */ |
||
14024 |
/* Amalgamated: #include "mjs/src/mjs_primitive.h" */ |
||
14025 |
|||
14026 |
2 |
mjs_val_t mjs_mk_null(void) { |
|
14027 |
2 |
return MJS_NULL; |
|
14028 |
} |
||
14029 |
|||
14030 |
3443 |
int mjs_is_null(mjs_val_t v) { |
|
14031 |
3443 |
return v == MJS_NULL; |
|
14032 |
} |
||
14033 |
|||
14034 |
5 |
mjs_val_t mjs_mk_undefined(void) { |
|
14035 |
5 |
return MJS_UNDEFINED; |
|
14036 |
} |
||
14037 |
|||
14038 |
55 |
int mjs_is_undefined(mjs_val_t v) { |
|
14039 |
55 |
return v == MJS_UNDEFINED; |
|
14040 |
} |
||
14041 |
|||
14042 |
37 |
mjs_val_t mjs_mk_number(struct mjs *mjs, double v) { |
|
14043 |
mjs_val_t res; |
||
14044 |
(void) mjs; |
||
14045 |
/* not every NaN is a JS NaN */ |
||
14046 |
✗✓ | 37 |
if (isnan(v)) { |
14047 |
res = MJS_TAG_NAN; |
||
14048 |
} else { |
||
14049 |
union { |
||
14050 |
double d; |
||
14051 |
mjs_val_t r; |
||
14052 |
} u; |
||
14053 |
37 |
u.d = v; |
|
14054 |
37 |
res = u.r; |
|
14055 |
} |
||
14056 |
37 |
return res; |
|
14057 |
} |
||
14058 |
|||
14059 |
105 |
static double get_double(mjs_val_t v) { |
|
14060 |
union { |
||
14061 |
double d; |
||
14062 |
mjs_val_t v; |
||
14063 |
} u; |
||
14064 |
105 |
u.v = v; |
|
14065 |
/* Due to NaN packing, any non-numeric value is already a valid NaN value */ |
||
14066 |
105 |
return u.d; |
|
14067 |
} |
||
14068 |
|||
14069 |
15 |
double mjs_get_double(struct mjs *mjs, mjs_val_t v) { |
|
14070 |
(void) mjs; |
||
14071 |
15 |
return get_double(v); |
|
14072 |
} |
||
14073 |
|||
14074 |
int mjs_get_int(struct mjs *mjs, mjs_val_t v) { |
||
14075 |
(void) mjs; |
||
14076 |
/* |
||
14077 |
* NOTE(dfrank): without double cast, all numbers >= 0x80000000 are always |
||
14078 |
* converted to exactly 0x80000000. |
||
14079 |
*/ |
||
14080 |
return (int) (unsigned int) get_double(v); |
||
14081 |
} |
||
14082 |
|||
14083 |
int32_t mjs_get_int32(struct mjs *mjs, mjs_val_t v) { |
||
14084 |
(void) mjs; |
||
14085 |
return (int32_t) get_double(v); |
||
14086 |
} |
||
14087 |
|||
14088 |
90 |
int mjs_is_number(mjs_val_t v) { |
|
14089 |
✓✗✓✓ |
90 |
return v == MJS_TAG_NAN || !isnan(get_double(v)); |
14090 |
} |
||
14091 |
|||
14092 |
3 |
mjs_val_t mjs_mk_boolean(struct mjs *mjs, int v) { |
|
14093 |
(void) mjs; |
||
14094 |
✓✓ | 3 |
return (v ? 1 : 0) | MJS_TAG_BOOLEAN; |
14095 |
} |
||
14096 |
|||
14097 |
2 |
int mjs_get_bool(struct mjs *mjs, mjs_val_t v) { |
|
14098 |
(void) mjs; |
||
14099 |
✓✗ | 2 |
if (mjs_is_boolean(v)) { |
14100 |
2 |
return v & 1; |
|
14101 |
} else { |
||
14102 |
return 0; |
||
14103 |
} |
||
14104 |
} |
||
14105 |
|||
14106 |
65 |
int mjs_is_boolean(mjs_val_t v) { |
|
14107 |
65 |
return (v & MJS_TAG_MASK) == MJS_TAG_BOOLEAN; |
|
14108 |
} |
||
14109 |
|||
14110 |
#define MJS_IS_POINTER_LEGIT(n) \ |
||
14111 |
(((n) &MJS_TAG_MASK) == 0 || ((n) &MJS_TAG_MASK) == (~0 & MJS_TAG_MASK)) |
||
14112 |
|||
14113 |
1092 |
MJS_PRIVATE mjs_val_t mjs_pointer_to_value(struct mjs *mjs, void *p) { |
|
14114 |
1092 |
uint64_t n = ((uint64_t)(uintptr_t) p); |
|
14115 |
|||
14116 |
✗✓✗✗ |
1092 |
if (!MJS_IS_POINTER_LEGIT(n)) { |
14117 |
mjs_prepend_errorf(mjs, MJS_TYPE_ERROR, "invalid pointer value: %p", p); |
||
14118 |
} |
||
14119 |
1092 |
return n & ~MJS_TAG_MASK; |
|
14120 |
} |
||
14121 |
|||
14122 |
235 |
MJS_PRIVATE mjs_val_t mjs_legit_pointer_to_value(void *p) { |
|
14123 |
235 |
uint64_t n = ((uint64_t)(uintptr_t) p); |
|
14124 |
|||
14125 |
✗✓✗✗ |
235 |
assert(MJS_IS_POINTER_LEGIT(n)); |
14126 |
235 |
return n & ~MJS_TAG_MASK; |
|
14127 |
} |
||
14128 |
|||
14129 |
6538 |
MJS_PRIVATE void *get_ptr(mjs_val_t v) { |
|
14130 |
6538 |
return (void *) (uintptr_t)(v & 0xFFFFFFFFFFFFUL); |
|
14131 |
} |
||
14132 |
|||
14133 |
void *mjs_get_ptr(struct mjs *mjs, mjs_val_t v) { |
||
14134 |
(void) mjs; |
||
14135 |
if (!mjs_is_foreign(v)) { |
||
14136 |
return NULL; |
||
14137 |
} |
||
14138 |
return get_ptr(v); |
||
14139 |
} |
||
14140 |
|||
14141 |
mjs_val_t mjs_mk_foreign(struct mjs *mjs, void *p) { |
||
14142 |
(void) mjs; |
||
14143 |
return mjs_pointer_to_value(mjs, p) | MJS_TAG_FOREIGN; |
||
14144 |
} |
||
14145 |
|||
14146 |
1092 |
mjs_val_t mjs_mk_foreign_func(struct mjs *mjs, mjs_func_ptr_t fn) { |
|
14147 |
union { |
||
14148 |
mjs_func_ptr_t fn; |
||
14149 |
void *p; |
||
14150 |
} u; |
||
14151 |
1092 |
u.fn = fn; |
|
14152 |
(void) mjs; |
||
14153 |
1092 |
return mjs_pointer_to_value(mjs, u.p) | MJS_TAG_FOREIGN; |
|
14154 |
} |
||
14155 |
|||
14156 |
83 |
int mjs_is_foreign(mjs_val_t v) { |
|
14157 |
83 |
return (v & MJS_TAG_MASK) == MJS_TAG_FOREIGN; |
|
14158 |
} |
||
14159 |
|||
14160 |
mjs_val_t mjs_mk_function(struct mjs *mjs, size_t off) { |
||
14161 |
(void) mjs; |
||
14162 |
return (mjs_val_t) off | MJS_TAG_FUNCTION; |
||
14163 |
} |
||
14164 |
|||
14165 |
56 |
int mjs_is_function(mjs_val_t v) { |
|
14166 |
56 |
return (v & MJS_TAG_MASK) == MJS_TAG_FUNCTION; |
|
14167 |
} |
||
14168 |
|||
14169 |
MJS_PRIVATE void mjs_op_isnan(struct mjs *mjs) { |
||
14170 |
mjs_val_t ret = MJS_UNDEFINED; |
||
14171 |
mjs_val_t val = mjs_arg(mjs, 0); |
||
14172 |
|||
14173 |
ret = mjs_mk_boolean(mjs, val == MJS_TAG_NAN); |
||
14174 |
|||
14175 |
mjs_return(mjs, ret); |
||
14176 |
} |
||
14177 |
#ifdef MJS_MODULE_LINES |
||
14178 |
#line 1 "mjs/src/mjs_string.c" |
||
14179 |
#endif |
||
14180 |
/* |
||
14181 |
* Copyright (c) 2017 Cesanta Software Limited |
||
14182 |
* All rights reserved |
||
14183 |
*/ |
||
14184 |
|||
14185 |
/* Amalgamated: #include "mjs/src/mjs_string.h" */ |
||
14186 |
/* Amalgamated: #include "common/cs_varint.h" */ |
||
14187 |
/* Amalgamated: #include "common/mg_str.h" */ |
||
14188 |
/* Amalgamated: #include "mjs/src/mjs_conversion.h" */ |
||
14189 |
/* Amalgamated: #include "mjs/src/mjs_core.h" */ |
||
14190 |
/* Amalgamated: #include "mjs/src/mjs_internal.h" */ |
||
14191 |
/* Amalgamated: #include "mjs/src/mjs_primitive.h" */ |
||
14192 |
/* Amalgamated: #include "mjs/src/mjs_util.h" */ |
||
14193 |
|||
14194 |
// No UTF |
||
14195 |
typedef unsigned short Rune; |
||
14196 |
28 |
static int chartorune(Rune *rune, const char *str) { |
|
14197 |
28 |
*rune = *(unsigned char *) str; |
|
14198 |
28 |
return 1; |
|
14199 |
} |
||
14200 |
28 |
static int runetochar(char *str, Rune *rune) { |
|
14201 |
28 |
str[0] = (char) *rune; |
|
14202 |
28 |
return 1; |
|
14203 |
} |
||
14204 |
|||
14205 |
#ifndef MJS_STRING_BUF_RESERVE |
||
14206 |
#define MJS_STRING_BUF_RESERVE 100 |
||
14207 |
#endif |
||
14208 |
|||
14209 |
MJS_PRIVATE size_t unescape(const char *s, size_t len, char *to); |
||
14210 |
|||
14211 |
MJS_PRIVATE void embed_string(struct mbuf *m, size_t offset, const char *p, |
||
14212 |
size_t len, uint8_t /*enum embstr_flags*/ flags); |
||
14213 |
|||
14214 |
/* TODO(lsm): NaN payload location depends on endianness, make crossplatform */ |
||
14215 |
#define GET_VAL_NAN_PAYLOAD(v) ((char *) &(v)) |
||
14216 |
|||
14217 |
9912 |
int mjs_is_string(mjs_val_t v) { |
|
14218 |
9912 |
uint64_t t = v & MJS_TAG_MASK; |
|
14219 |
✓✗✓✓ |
15782 |
return t == MJS_TAG_STRING_I || t == MJS_TAG_STRING_F || |
14220 |
✓✓✓✓ ✗✓ |
13029 |
t == MJS_TAG_STRING_O || t == MJS_TAG_STRING_5 || |
14221 |
t == MJS_TAG_STRING_D; |
||
14222 |
} |
||
14223 |
|||
14224 |
1494 |
mjs_val_t mjs_mk_string(struct mjs *mjs, const char *p, size_t len, int copy) { |
|
14225 |
struct mbuf *m; |
||
14226 |
1494 |
mjs_val_t offset, tag = MJS_TAG_STRING_F; |
|
14227 |
✓✓ | 1494 |
if (len == 0) { |
14228 |
/* |
||
14229 |
* Zero length for foreign string has a special meaning (that the foreign |
||
14230 |
* string is not inlined into mjs_val_t), so when creating a zero-length |
||
14231 |
* string, we always assume it'll be owned. Since the length is zero, it |
||
14232 |
* doesn't matter anyway. |
||
14233 |
*/ |
||
14234 |
4 |
copy = 1; |
|
14235 |
} |
||
14236 |
✓✗ | 1494 |
m = copy ? &mjs->owned_strings : &mjs->foreign_strings; |
14237 |
1494 |
offset = m->len; |
|
14238 |
|||
14239 |
✓✓ | 1494 |
if (len == ~((size_t) 0)) len = strlen(p); |
14240 |
|||
14241 |
✓✗ | 1494 |
if (copy) { |
14242 |
/* owned string */ |
||
14243 |
✓✓ | 1494 |
if (len <= 4) { |
14244 |
706 |
char *s = GET_VAL_NAN_PAYLOAD(offset) + 1; |
|
14245 |
706 |
offset = 0; |
|
14246 |
✓✗ | 706 |
if (p != 0) { |
14247 |
706 |
memcpy(s, p, len); |
|
14248 |
} |
||
14249 |
706 |
s[-1] = len; |
|
14250 |
706 |
tag = MJS_TAG_STRING_I; |
|
14251 |
✓✓ | 788 |
} else if (len == 5) { |
14252 |
316 |
char *s = GET_VAL_NAN_PAYLOAD(offset); |
|
14253 |
316 |
offset = 0; |
|
14254 |
✓✗ | 316 |
if (p != 0) { |
14255 |
316 |
memcpy(s, p, len); |
|
14256 |
} |
||
14257 |
316 |
tag = MJS_TAG_STRING_5; |
|
14258 |
// } else if ((dict_index = v_find_string_in_dictionary(p, len)) >= 0) { |
||
14259 |
// offset = 0; |
||
14260 |
// GET_VAL_NAN_PAYLOAD(offset)[0] = dict_index; |
||
14261 |
// tag = MJS_TAG_STRING_D; |
||
14262 |
} else { |
||
14263 |
✓✓ | 472 |
if (gc_strings_is_gc_needed(mjs)) { |
14264 |
78 |
mjs->need_gc = 1; |
|
14265 |
} |
||
14266 |
|||
14267 |
/* |
||
14268 |
* Before embedding new string, check if the reallocation is needed. If |
||
14269 |
* so, perform the reallocation by calling `mbuf_resize` manually, since |
||
14270 |
* we need to preallocate some extra space (`MJS_STRING_BUF_RESERVE`) |
||
14271 |
*/ |
||
14272 |
✓✓ | 472 |
if ((m->len + len) > m->size) { |
14273 |
78 |
char *prev_buf = m->buf; |
|
14274 |
78 |
mbuf_resize(m, m->len + len + MJS_STRING_BUF_RESERVE); |
|
14275 |
|||
14276 |
/* |
||
14277 |
* There is a corner case: when the source pointer is located within |
||
14278 |
* the mbuf. In this case, we should adjust the pointer, because it |
||
14279 |
* might have just been reallocated. |
||
14280 |
*/ |
||
14281 |
✗✓✗✗ |
78 |
if (p >= prev_buf && p < (prev_buf + m->len)) { |
14282 |
p += (m->buf - prev_buf); |
||
14283 |
} |
||
14284 |
} |
||
14285 |
|||
14286 |
472 |
embed_string(m, m->len, p, len, EMBSTR_ZERO_TERM); |
|
14287 |
472 |
tag = MJS_TAG_STRING_O; |
|
14288 |
} |
||
14289 |
} else { |
||
14290 |
/* foreign string */ |
||
14291 |
if (sizeof(void *) <= 4 && len <= (1 << 15)) { |
||
14292 |
/* small foreign strings can fit length and ptr in the mjs_val_t */ |
||
14293 |
offset = (uint64_t) len << 32 | (uint64_t)(uintptr_t) p; |
||
14294 |
} else { |
||
14295 |
/* bigger strings need indirection that uses ram */ |
||
14296 |
size_t pos = m->len; |
||
14297 |
size_t llen = cs_varint_llen(len); |
||
14298 |
|||
14299 |
/* allocate space for len and ptr */ |
||
14300 |
mbuf_insert(m, pos, NULL, llen + sizeof(p)); |
||
14301 |
|||
14302 |
cs_varint_encode(len, (uint8_t *) (m->buf + pos), llen); |
||
14303 |
memcpy(m->buf + pos + llen, &p, sizeof(p)); |
||
14304 |
} |
||
14305 |
tag = MJS_TAG_STRING_F; |
||
14306 |
} |
||
14307 |
|||
14308 |
/* NOTE(lsm): don't use pointer_to_value, 32-bit ptrs will truncate */ |
||
14309 |
1494 |
return (offset & ~MJS_TAG_MASK) | tag; |
|
14310 |
} |
||
14311 |
|||
14312 |
/* Get a pointer to string and string length. */ |
||
14313 |
8400 |
const char *mjs_get_string(struct mjs *mjs, mjs_val_t *v, size_t *sizep) { |
|
14314 |
8400 |
uint64_t tag = v[0] & MJS_TAG_MASK; |
|
14315 |
8400 |
const char *p = NULL; |
|
14316 |
8400 |
size_t size = 0, llen; |
|
14317 |
|||
14318 |
✗✓ | 8400 |
if (!mjs_is_string(*v)) { |
14319 |
goto clean; |
||
14320 |
} |
||
14321 |
|||
14322 |
✓✓ | 8400 |
if (tag == MJS_TAG_STRING_I) { |
14323 |
4000 |
p = GET_VAL_NAN_PAYLOAD(*v) + 1; |
|
14324 |
4000 |
size = p[-1]; |
|
14325 |
✓✓ | 4400 |
} else if (tag == MJS_TAG_STRING_5) { |
14326 |
1651 |
p = GET_VAL_NAN_PAYLOAD(*v); |
|
14327 |
1651 |
size = 5; |
|
14328 |
// } else if (tag == MJS_TAG_STRING_D) { |
||
14329 |
// int index = ((unsigned char *) GET_VAL_NAN_PAYLOAD(*v))[0]; |
||
14330 |
// size = v_dictionary_strings[index].len; |
||
14331 |
// p = v_dictionary_strings[index].p; |
||
14332 |
✓✗ | 2749 |
} else if (tag == MJS_TAG_STRING_O) { |
14333 |
2749 |
size_t offset = (size_t) gc_string_mjs_val_to_offset(*v); |
|
14334 |
2749 |
char *s = mjs->owned_strings.buf + offset; |
|
14335 |
2749 |
uint64_t v = 0; |
|
14336 |
✓✗✓✗ |
5498 |
if (offset < mjs->owned_strings.len && |
14337 |
2749 |
cs_varint_decode((uint8_t *) s, mjs->owned_strings.len - offset, &v, |
|
14338 |
&llen)) { |
||
14339 |
2749 |
size = v; |
|
14340 |
2749 |
p = s + llen; |
|
14341 |
} else { |
||
14342 |
goto clean; |
||
14343 |
} |
||
14344 |
} else if (tag == MJS_TAG_STRING_F) { |
||
14345 |
/* |
||
14346 |
* short foreign strings on <=32-bit machines can be encoded in a compact |
||
14347 |
* form: |
||
14348 |
* |
||
14349 |
* 7 6 5 4 3 2 1 0 |
||
14350 |
* 11111111|1111tttt|llllllll|llllllll|ssssssss|ssssssss|ssssssss|ssssssss |
||
14351 |
* |
||
14352 |
* Strings longer than 2^26 will be indireceted through the foreign_strings |
||
14353 |
* mbuf. |
||
14354 |
* |
||
14355 |
* We don't use a different tag to represent those two cases. Instead, all |
||
14356 |
* foreign strings represented with the help of the foreign_strings mbuf |
||
14357 |
* will have the upper 16-bits of the payload set to zero. This allows us to |
||
14358 |
* represent up to 477 million foreign strings longer than 64k. |
||
14359 |
*/ |
||
14360 |
uint16_t len = (*v >> 32) & 0xFFFF; |
||
14361 |
if (sizeof(void *) <= 4 && len != 0) { |
||
14362 |
size = (size_t) len; |
||
14363 |
p = (const char *) (uintptr_t) *v; |
||
14364 |
} else { |
||
14365 |
size_t offset = (size_t) gc_string_mjs_val_to_offset(*v); |
||
14366 |
char *s = mjs->foreign_strings.buf + offset; |
||
14367 |
uint64_t v = 0; |
||
14368 |
if (offset < mjs->foreign_strings.len && |
||
14369 |
cs_varint_decode((uint8_t *) s, mjs->foreign_strings.len - offset, &v, |
||
14370 |
&llen)) { |
||
14371 |
size = v; |
||
14372 |
memcpy((char **) &p, s + llen, sizeof(p)); |
||
14373 |
} else { |
||
14374 |
goto clean; |
||
14375 |
} |
||
14376 |
} |
||
14377 |
} else { |
||
14378 |
assert(0); |
||
14379 |
} |
||
14380 |
|||
14381 |
clean: |
||
14382 |
✓✗ | 8400 |
if (sizep != NULL) { |
14383 |
8400 |
*sizep = size; |
|
14384 |
} |
||
14385 |
8400 |
return p; |
|
14386 |
} |
||
14387 |
|||
14388 |
40 |
const char *mjs_get_cstring(struct mjs *mjs, mjs_val_t *value) { |
|
14389 |
size_t size; |
||
14390 |
40 |
const char *s = mjs_get_string(mjs, value, &size); |
|
14391 |
✗✓ | 40 |
if (s == NULL) return NULL; |
14392 |
✓✗✗✓ |
40 |
if (s[size] != 0 || strlen(s) != size) { |
14393 |
return NULL; |
||
14394 |
} |
||
14395 |
40 |
return s; |
|
14396 |
} |
||
14397 |
|||
14398 |
8313 |
int mjs_strcmp(struct mjs *mjs, mjs_val_t *a, const char *b, size_t len) { |
|
14399 |
size_t n; |
||
14400 |
const char *s; |
||
14401 |
✓✓ | 8313 |
if (len == (size_t) ~0) len = strlen(b); |
14402 |
8313 |
s = mjs_get_string(mjs, a, &n); |
|
14403 |
✓✓ | 8313 |
if (n != len) { |
14404 |
6981 |
return n - len; |
|
14405 |
} |
||
14406 |
1332 |
return strncmp(s, b, len); |
|
14407 |
} |
||
14408 |
|||
14409 |
MJS_PRIVATE unsigned long cstr_to_ulong(const char *s, size_t len, int *ok) { |
||
14410 |
char *e; |
||
14411 |
unsigned long res = strtoul(s, &e, 10); |
||
14412 |
*ok = (e == s + len) && len != 0; |
||
14413 |
return res; |
||
14414 |
} |
||
14415 |
|||
14416 |
MJS_PRIVATE mjs_err_t |
||
14417 |
str_to_ulong(struct mjs *mjs, mjs_val_t v, int *ok, unsigned long *res) { |
||
14418 |
enum mjs_err ret = MJS_OK; |
||
14419 |
size_t len = 0; |
||
14420 |
const char *p = mjs_get_string(mjs, &v, &len); |
||
14421 |
*res = cstr_to_ulong(p, len, ok); |
||
14422 |
|||
14423 |
return ret; |
||
14424 |
} |
||
14425 |
|||
14426 |
MJS_PRIVATE int s_cmp(struct mjs *mjs, mjs_val_t a, mjs_val_t b) { |
||
14427 |
size_t a_len, b_len; |
||
14428 |
const char *a_ptr, *b_ptr; |
||
14429 |
|||
14430 |
a_ptr = mjs_get_string(mjs, &a, &a_len); |
||
14431 |
b_ptr = mjs_get_string(mjs, &b, &b_len); |
||
14432 |
|||
14433 |
if (a_len == b_len) { |
||
14434 |
return memcmp(a_ptr, b_ptr, a_len); |
||
14435 |
} |
||
14436 |
if (a_len > b_len) { |
||
14437 |
return 1; |
||
14438 |
} else if (a_len < b_len) { |
||
14439 |
return -1; |
||
14440 |
} else { |
||
14441 |
return 0; |
||
14442 |
} |
||
14443 |
} |
||
14444 |
|||
14445 |
MJS_PRIVATE mjs_val_t s_concat(struct mjs *mjs, mjs_val_t a, mjs_val_t b) { |
||
14446 |
size_t a_len, b_len, res_len; |
||
14447 |
const char *a_ptr, *b_ptr, *res_ptr; |
||
14448 |
mjs_val_t res; |
||
14449 |
|||
14450 |
/* Find out lengths of both srtings */ |
||
14451 |
a_ptr = mjs_get_string(mjs, &a, &a_len); |
||
14452 |
b_ptr = mjs_get_string(mjs, &b, &b_len); |
||
14453 |
|||
14454 |
/* Create a placeholder string */ |
||
14455 |
res = mjs_mk_string(mjs, NULL, a_len + b_len, 1); |
||
14456 |
|||
14457 |
/* mjs_mk_string() may have reallocated mbuf - revalidate pointers */ |
||
14458 |
a_ptr = mjs_get_string(mjs, &a, &a_len); |
||
14459 |
b_ptr = mjs_get_string(mjs, &b, &b_len); |
||
14460 |
|||
14461 |
/* Copy strings into the placeholder */ |
||
14462 |
res_ptr = mjs_get_string(mjs, &res, &res_len); |
||
14463 |
memcpy((char *) res_ptr, a_ptr, a_len); |
||
14464 |
memcpy((char *) res_ptr + a_len, b_ptr, b_len); |
||
14465 |
|||
14466 |
return res; |
||
14467 |
} |
||
14468 |
|||
14469 |
MJS_PRIVATE void mjs_string_slice(struct mjs *mjs) { |
||
14470 |
int nargs = mjs_nargs(mjs); |
||
14471 |
mjs_val_t ret = mjs_mk_number(mjs, 0); |
||
14472 |
mjs_val_t beginSlice_v = MJS_UNDEFINED; |
||
14473 |
mjs_val_t endSlice_v = MJS_UNDEFINED; |
||
14474 |
int beginSlice = 0; |
||
14475 |
int endSlice = 0; |
||
14476 |
size_t size; |
||
14477 |
const char *s = NULL; |
||
14478 |
|||
14479 |
/* get string from `this` */ |
||
14480 |
if (!mjs_check_arg(mjs, -1 /*this*/, "this", MJS_TYPE_STRING, NULL)) { |
||
14481 |
goto clean; |
||
14482 |
} |
||
14483 |
s = mjs_get_string(mjs, &mjs->vals.this_obj, &size); |
||
14484 |
|||
14485 |
/* get idx from arg 0 */ |
||
14486 |
if (!mjs_check_arg(mjs, 0, "beginSlice", MJS_TYPE_NUMBER, &beginSlice_v)) { |
||
14487 |
goto clean; |
||
14488 |
} |
||
14489 |
beginSlice = mjs_normalize_idx(mjs_get_int(mjs, beginSlice_v), size); |
||
14490 |
|||
14491 |
if (nargs >= 2) { |
||
14492 |
/* endSlice is given; use it */ |
||
14493 |
/* get idx from arg 0 */ |
||
14494 |
if (!mjs_check_arg(mjs, 1, "endSlice", MJS_TYPE_NUMBER, &endSlice_v)) { |
||
14495 |
goto clean; |
||
14496 |
} |
||
14497 |
endSlice = mjs_normalize_idx(mjs_get_int(mjs, endSlice_v), size); |
||
14498 |
} else { |
||
14499 |
/* endSlice is not given; assume the end of the string */ |
||
14500 |
endSlice = size; |
||
14501 |
} |
||
14502 |
|||
14503 |
if (endSlice < beginSlice) { |
||
14504 |
endSlice = beginSlice; |
||
14505 |
} |
||
14506 |
|||
14507 |
ret = mjs_mk_string(mjs, s + beginSlice, endSlice - beginSlice, 1); |
||
14508 |
|||
14509 |
clean: |
||
14510 |
mjs_return(mjs, ret); |
||
14511 |
} |
||
14512 |
|||
14513 |
MJS_PRIVATE void mjs_string_index_of(struct mjs *mjs) { |
||
14514 |
mjs_val_t ret = mjs_mk_number(mjs, -1); |
||
14515 |
mjs_val_t substr_v = MJS_UNDEFINED; |
||
14516 |
mjs_val_t idx_v = MJS_UNDEFINED; |
||
14517 |
int idx = 0; |
||
14518 |
const char *str = NULL, *substr = NULL; |
||
14519 |
size_t str_len = 0, substr_len = 0; |
||
14520 |
|||
14521 |
if (!mjs_check_arg(mjs, -1 /* this */, "this", MJS_TYPE_STRING, NULL)) { |
||
14522 |
goto clean; |
||
14523 |
} |
||
14524 |
str = mjs_get_string(mjs, &mjs->vals.this_obj, &str_len); |
||
14525 |
|||
14526 |
if (!mjs_check_arg(mjs, 0, "searchValue", MJS_TYPE_STRING, &substr_v)) { |
||
14527 |
goto clean; |
||
14528 |
} |
||
14529 |
substr = mjs_get_string(mjs, &substr_v, &substr_len); |
||
14530 |
if (mjs_nargs(mjs) > 1) { |
||
14531 |
if (!mjs_check_arg(mjs, 1, "fromIndex", MJS_TYPE_NUMBER, &idx_v)) { |
||
14532 |
goto clean; |
||
14533 |
} |
||
14534 |
idx = mjs_get_int(mjs, idx_v); |
||
14535 |
if (idx < 0) idx = 0; |
||
14536 |
if ((size_t) idx > str_len) idx = str_len; |
||
14537 |
} |
||
14538 |
{ |
||
14539 |
const char *substr_p; |
||
14540 |
struct mg_str mgstr, mgsubstr; |
||
14541 |
mgstr.p = str + idx; |
||
14542 |
mgstr.len = str_len - idx; |
||
14543 |
mgsubstr.p = substr; |
||
14544 |
mgsubstr.len = substr_len; |
||
14545 |
substr_p = mg_strstr(mgstr, mgsubstr); |
||
14546 |
if (substr_p != NULL) { |
||
14547 |
ret = mjs_mk_number(mjs, (int) (substr_p - str)); |
||
14548 |
} |
||
14549 |
} |
||
14550 |
|||
14551 |
clean: |
||
14552 |
mjs_return(mjs, ret); |
||
14553 |
} |
||
14554 |
|||
14555 |
MJS_PRIVATE void mjs_string_char_code_at(struct mjs *mjs) { |
||
14556 |
mjs_val_t ret = MJS_UNDEFINED; |
||
14557 |
mjs_val_t idx_v = MJS_UNDEFINED; |
||
14558 |
int idx = 0; |
||
14559 |
size_t size; |
||
14560 |
const char *s = NULL; |
||
14561 |
|||
14562 |
/* get string from `this` */ |
||
14563 |
if (!mjs_check_arg(mjs, -1 /*this*/, "this", MJS_TYPE_STRING, NULL)) { |
||
14564 |
goto clean; |
||
14565 |
} |
||
14566 |
s = mjs_get_string(mjs, &mjs->vals.this_obj, &size); |
||
14567 |
|||
14568 |
/* get idx from arg 0 */ |
||
14569 |
if (!mjs_check_arg(mjs, 0, "index", MJS_TYPE_NUMBER, &idx_v)) { |
||
14570 |
goto clean; |
||
14571 |
} |
||
14572 |
idx = mjs_normalize_idx(mjs_get_int(mjs, idx_v), size); |
||
14573 |
if (idx >= 0 && idx < (int) size) { |
||
14574 |
ret = mjs_mk_number(mjs, ((unsigned char *) s)[idx]); |
||
14575 |
} |
||
14576 |
|||
14577 |
clean: |
||
14578 |
mjs_return(mjs, ret); |
||
14579 |
} |
||
14580 |
|||
14581 |
MJS_PRIVATE void mjs_mkstr(struct mjs *mjs) { |
||
14582 |
int nargs = mjs_nargs(mjs); |
||
14583 |
mjs_val_t ret = MJS_UNDEFINED; |
||
14584 |
|||
14585 |
char *ptr = NULL; |
||
14586 |
int offset = 0; |
||
14587 |
int len = 0; |
||
14588 |
int copy = 0; |
||
14589 |
|||
14590 |
mjs_val_t ptr_v = MJS_UNDEFINED; |
||
14591 |
mjs_val_t offset_v = MJS_UNDEFINED; |
||
14592 |
mjs_val_t len_v = MJS_UNDEFINED; |
||
14593 |
mjs_val_t copy_v = MJS_UNDEFINED; |
||
14594 |
|||
14595 |
if (nargs == 2) { |
||
14596 |
ptr_v = mjs_arg(mjs, 0); |
||
14597 |
len_v = mjs_arg(mjs, 1); |
||
14598 |
} else if (nargs == 3) { |
||
14599 |
ptr_v = mjs_arg(mjs, 0); |
||
14600 |
offset_v = mjs_arg(mjs, 1); |
||
14601 |
len_v = mjs_arg(mjs, 2); |
||
14602 |
} else if (nargs == 4) { |
||
14603 |
ptr_v = mjs_arg(mjs, 0); |
||
14604 |
offset_v = mjs_arg(mjs, 1); |
||
14605 |
len_v = mjs_arg(mjs, 2); |
||
14606 |
copy_v = mjs_arg(mjs, 3); |
||
14607 |
} else { |
||
14608 |
mjs_prepend_errorf(mjs, MJS_TYPE_ERROR, |
||
14609 |
"mkstr takes 2, 3 or 4 arguments: (ptr, len), (ptr, " |
||
14610 |
"offset, len) or (ptr, offset, len, copy)"); |
||
14611 |
goto clean; |
||
14612 |
} |
||
14613 |
|||
14614 |
if (!mjs_is_foreign(ptr_v)) { |
||
14615 |
mjs_prepend_errorf(mjs, MJS_TYPE_ERROR, "ptr should be a foreign pointer"); |
||
14616 |
goto clean; |
||
14617 |
} |
||
14618 |
|||
14619 |
if (offset_v != MJS_UNDEFINED && !mjs_is_number(offset_v)) { |
||
14620 |
mjs_prepend_errorf(mjs, MJS_TYPE_ERROR, "offset should be a number"); |
||
14621 |
goto clean; |
||
14622 |
} |
||
14623 |
|||
14624 |
if (!mjs_is_number(len_v)) { |
||
14625 |
mjs_prepend_errorf(mjs, MJS_TYPE_ERROR, "len should be a number"); |
||
14626 |
goto clean; |
||
14627 |
} |
||
14628 |
|||
14629 |
copy = mjs_is_truthy(mjs, copy_v); |
||
14630 |
|||
14631 |
/* all arguments are fine */ |
||
14632 |
|||
14633 |
ptr = (char *) mjs_get_ptr(mjs, ptr_v); |
||
14634 |
if (offset_v != MJS_UNDEFINED) { |
||
14635 |
offset = mjs_get_int(mjs, offset_v); |
||
14636 |
} |
||
14637 |
len = mjs_get_int(mjs, len_v); |
||
14638 |
|||
14639 |
ret = mjs_mk_string(mjs, ptr + offset, len, copy); |
||
14640 |
|||
14641 |
clean: |
||
14642 |
mjs_return(mjs, ret); |
||
14643 |
} |
||
14644 |
|||
14645 |
enum unescape_error { |
||
14646 |
SLRE_INVALID_HEX_DIGIT, |
||
14647 |
SLRE_INVALID_ESC_CHAR, |
||
14648 |
SLRE_UNTERM_ESC_SEQ, |
||
14649 |
}; |
||
14650 |
|||
14651 |
static int hex(int c) { |
||
14652 |
if (c >= '0' && c <= '9') return c - '0'; |
||
14653 |
if (c >= 'a' && c <= 'f') return c - 'a' + 10; |
||
14654 |
if (c >= 'A' && c <= 'F') return c - 'A' + 10; |
||
14655 |
return -SLRE_INVALID_HEX_DIGIT; |
||
14656 |
} |
||
14657 |
|||
14658 |
10 |
static int nextesc(const char **p) { |
|
14659 |
10 |
const unsigned char *s = (unsigned char *) (*p)++; |
|
14660 |
✗✗✗✗ ✗✗✓✗ ✓✓✗✓ |
10 |
switch (*s) { |
14661 |
case 0: |
||
14662 |
return -SLRE_UNTERM_ESC_SEQ; |
||
14663 |
case 'c': |
||
14664 |
++*p; |
||
14665 |
return *s & 31; |
||
14666 |
case 'b': |
||
14667 |
return '\b'; |
||
14668 |
case 't': |
||
14669 |
return '\t'; |
||
14670 |
case 'n': |
||
14671 |
return '\n'; |
||
14672 |
case 'v': |
||
14673 |
return '\v'; |
||
14674 |
case 'f': |
||
14675 |
2 |
return '\f'; |
|
14676 |
case 'r': |
||
14677 |
return '\r'; |
||
14678 |
case '\\': |
||
14679 |
4 |
return '\\'; |
|
14680 |
case 'u': |
||
14681 |
✗✓✗✗ ✗✗✗✗ |
2 |
if (isxdigit(s[1]) && isxdigit(s[2]) && isxdigit(s[3]) && |
14682 |
isxdigit(s[4])) { |
||
14683 |
(*p) += 4; |
||
14684 |
return hex(s[1]) << 12 | hex(s[2]) << 8 | hex(s[3]) << 4 | hex(s[4]); |
||
14685 |
} |
||
14686 |
2 |
return -SLRE_INVALID_HEX_DIGIT; |
|
14687 |
case 'x': |
||
14688 |
if (isxdigit(s[1]) && isxdigit(s[2])) { |
||
14689 |
(*p) += 2; |
||
14690 |
return (hex(s[1]) << 4) | hex(s[2]); |
||
14691 |
} |
||
14692 |
return -SLRE_INVALID_HEX_DIGIT; |
||
14693 |
default: |
||
14694 |
2 |
return -SLRE_INVALID_ESC_CHAR; |
|
14695 |
} |
||
14696 |
} |
||
14697 |
|||
14698 |
50 |
MJS_PRIVATE size_t unescape(const char *s, size_t len, char *to) { |
|
14699 |
50 |
const char *end = s + len; |
|
14700 |
50 |
size_t n = 0; |
|
14701 |
char tmp[4]; |
||
14702 |
Rune r; |
||
14703 |
|||
14704 |
✓✓ | 126 |
while (s < end) { |
14705 |
26 |
s += chartorune(&r, s); |
|
14706 |
✓✓✓✓ |
26 |
if (r == '\\' && s < end) { |
14707 |
✓✗✓✓ |
16 |
switch (*s) { |
14708 |
case '"': |
||
14709 |
4 |
s++, r = '"'; |
|
14710 |
4 |
break; |
|
14711 |
case '\'': |
||
14712 |
s++, r = '\''; |
||
14713 |
break; |
||
14714 |
case '\n': |
||
14715 |
2 |
s++, r = '\n'; |
|
14716 |
2 |
break; |
|
14717 |
default: { |
||
14718 |
10 |
const char *tmp_s = s; |
|
14719 |
10 |
int i = nextesc(&s); |
|
14720 |
✓✓ | 10 |
switch (i) { |
14721 |
case -SLRE_INVALID_ESC_CHAR: |
||
14722 |
2 |
r = '\\'; |
|
14723 |
2 |
s = tmp_s; |
|
14724 |
✓✓ | 2 |
n += runetochar(to == NULL ? tmp : to + n, &r); |
14725 |
2 |
s += chartorune(&r, s); |
|
14726 |
2 |
break; |
|
14727 |
case -SLRE_INVALID_HEX_DIGIT: |
||
14728 |
default: |
||
14729 |
8 |
r = i; |
|
14730 |
} |
||
14731 |
} |
||
14732 |
} |
||
14733 |
} |
||
14734 |
✓✓ | 26 |
n += runetochar(to == NULL ? tmp : to + n, &r); |
14735 |
} |
||
14736 |
|||
14737 |
50 |
return n; |
|
14738 |
} |
||
14739 |
|||
14740 |
497 |
MJS_PRIVATE void embed_string(struct mbuf *m, size_t offset, const char *p, |
|
14741 |
size_t len, uint8_t /*enum embstr_flags*/ flags) { |
||
14742 |
497 |
char *old_base = m->buf; |
|
14743 |
✓✓✗✓ |
497 |
uint8_t p_backed_by_mbuf = p >= old_base && p < old_base + m->len; |
14744 |
✓✓ | 497 |
size_t n = (flags & EMBSTR_UNESCAPE) ? unescape(p, len, NULL) : len; |
14745 |
|||
14746 |
/* Calculate how many bytes length takes */ |
||
14747 |
497 |
size_t k = cs_varint_llen(n); |
|
14748 |
|||
14749 |
/* total length: varing length + string len + zero-term */ |
||
14750 |
497 |
size_t tot_len = k + n + !!(flags & EMBSTR_ZERO_TERM); |
|
14751 |
|||
14752 |
/* Allocate buffer */ |
||
14753 |
497 |
mbuf_insert(m, offset, NULL, tot_len); |
|
14754 |
|||
14755 |
/* Fixup p if it was relocated by mbuf_insert() above */ |
||
14756 |
✗✓ | 497 |
if (p_backed_by_mbuf) { |
14757 |
p += m->buf - old_base; |
||
14758 |
} |
||
14759 |
|||
14760 |
/* Write length */ |
||
14761 |
497 |
cs_varint_encode(n, (unsigned char *) m->buf + offset, k); |
|
14762 |
|||
14763 |
/* Write string */ |
||
14764 |
✓✗ | 497 |
if (p != 0) { |
14765 |
✓✓ | 497 |
if (flags & EMBSTR_UNESCAPE) { |
14766 |
25 |
unescape(p, len, m->buf + offset + k); |
|
14767 |
} else { |
||
14768 |
472 |
memcpy(m->buf + offset + k, p, len); |
|
14769 |
} |
||
14770 |
} |
||
14771 |
|||
14772 |
/* add NULL-terminator if needed */ |
||
14773 |
✓✓ | 497 |
if (flags & EMBSTR_ZERO_TERM) { |
14774 |
472 |
m->buf[offset + tot_len - 1] = '\0'; |
|
14775 |
} |
||
14776 |
497 |
} |
|
14777 |
#ifdef MJS_MODULE_LINES |
||
14778 |
#line 1 "mjs/src/mjs_tok.c" |
||
14779 |
#endif |
||
14780 |
/* |
||
14781 |
* Copyright (c) 2017 Cesanta Software Limited |
||
14782 |
* All rights reserved |
||
14783 |
*/ |
||
14784 |
|||
14785 |
#include <stdlib.h> |
||
14786 |
#include <string.h> |
||
14787 |
|||
14788 |
/* Amalgamated: #include "common/cs_dbg.h" */ |
||
14789 |
/* Amalgamated: #include "mjs/src/mjs_tok.h" */ |
||
14790 |
|||
14791 |
78 |
MJS_PRIVATE void pinit(const char *file_name, const char *buf, |
|
14792 |
struct pstate *p) { |
||
14793 |
78 |
memset(p, 0, sizeof(*p)); |
|
14794 |
78 |
p->line_no = 1; |
|
14795 |
78 |
p->last_emitted_line_no = 1; |
|
14796 |
78 |
p->file_name = file_name; |
|
14797 |
78 |
p->buf = p->pos = buf; |
|
14798 |
78 |
mbuf_init(&p->offset_lineno_map, 0); |
|
14799 |
78 |
} |
|
14800 |
|||
14801 |
// We're not relying on the target libc ctype, as it may incorrectly |
||
14802 |
// handle negative arguments, e.g. isspace(-1). |
||
14803 |
513 |
static int mjs_is_space(int c) { |
|
14804 |
✓✓✓✓ ✓✓✓✓ ✓✓✗✓ |
513 |
return c == ' ' || c == '\r' || c == '\n' || c == '\t' || c == '\f' || c == '\v'; |
14805 |
} |
||
14806 |
|||
14807 |
543 |
MJS_PRIVATE int mjs_is_digit(int c) { |
|
14808 |
✓✓✓✓ |
543 |
return c >= '0' && c <= '9'; |
14809 |
} |
||
14810 |
|||
14811 |
792 |
static int mjs_is_alpha(int c) { |
|
14812 |
✓✓✓✓ ✓✓✓✓ |
792 |
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); |
14813 |
} |
||
14814 |
|||
14815 |
731 |
MJS_PRIVATE int mjs_is_ident(int c) { |
|
14816 |
✓✓✓✓ ✓✓ |
731 |
return c == '_' || c == '$' || mjs_is_alpha(c); |
14817 |
} |
||
14818 |
|||
14819 |
// Try to parse a token that can take one or two chars. |
||
14820 |
464 |
static int longtok(struct pstate *p, const char *first_chars, |
|
14821 |
const char *second_chars) { |
||
14822 |
✓✓ | 464 |
if (strchr(first_chars, p->pos[0]) == NULL) return TOK_EOF; |
14823 |
✓✗✓✓ |
94 |
if (p->pos[1] != '\0' && strchr(second_chars, p->pos[1]) != NULL) { |
14824 |
33 |
p->tok.len++; |
|
14825 |
33 |
p->pos++; |
|
14826 |
33 |
return p->pos[-1] << 8 | p->pos[0]; |
|
14827 |
} |
||
14828 |
61 |
return p->pos[0]; |
|
14829 |
} |
||
14830 |
|||
14831 |
// Try to parse a token that takes exactly 3 chars. |
||
14832 |
478 |
static int longtok3(struct pstate *p, char a, char b, char c) { |
|
14833 |
✓✓✓✓ ✓✓ |
478 |
if (p->pos[0] == a && p->pos[1] == b && p->pos[2] == c) { |
14834 |
3 |
p->tok.len += 2; |
|
14835 |
3 |
p->pos += 2; |
|
14836 |
3 |
return p->pos[-2] << 16 | p->pos[-1] << 8 | p->pos[0]; |
|
14837 |
} |
||
14838 |
475 |
return TOK_EOF; |
|
14839 |
} |
||
14840 |
|||
14841 |
// Try to parse a token that takes exactly 4 chars. |
||
14842 |
95 |
static int longtok4(struct pstate *p, char a, char b, char c, char d) { |
|
14843 |
✓✓✓✓ ✗✓✗✗ |
95 |
if (p->pos[0] == a && p->pos[1] == b && p->pos[2] == c && p->pos[3] == d) { |
14844 |
p->tok.len += 3; |
||
14845 |
p->pos += 3; |
||
14846 |
return p->pos[-3] << 24 | p->pos[-2] << 16 | p->pos[-1] << 8 | p->pos[0]; |
||
14847 |
} |
||
14848 |
95 |
return TOK_EOF; |
|
14849 |
} |
||
14850 |
|||
14851 |
42 |
static int getnum(struct pstate *p) { |
|
14852 |
✓✓✓✓ |
42 |
if (p->pos[0] == '0' && p->pos[1] == 'x') { |
14853 |
// MSVC6 strtod cannot parse 0x... numbers, thus this ugly workaround. |
||
14854 |
1 |
strtoul(p->pos + 2, (char **) &p->pos, 16); |
|
14855 |
} else { |
||
14856 |
41 |
strtod(p->pos, (char **) &p->pos); |
|
14857 |
} |
||
14858 |
42 |
p->tok.len = p->pos - p->tok.ptr; |
|
14859 |
42 |
p->pos--; |
|
14860 |
42 |
return TOK_NUM; |
|
14861 |
} |
||
14862 |
|||
14863 |
107 |
static int is_reserved_word_token(const char *s, int len) { |
|
14864 |
107 |
const char *reserved[] = { |
|
14865 |
"break", "case", "catch", "continue", "debugger", "default", |
||
14866 |
"delete", "do", "else", "false", "finally", "for", |
||
14867 |
"function", "if", "in", "instanceof", "new", "null", |
||
14868 |
"return", "switch", "this", "throw", "true", "try", |
||
14869 |
"typeof", "var", "void", "while", "with", "let", |
||
14870 |
"undefined", NULL}; |
||
14871 |
int i; |
||
14872 |
✓✓ | 107 |
if (!mjs_is_alpha(s[0])) return 0; |
14873 |
✓✓ | 2356 |
for (i = 0; reserved[i] != NULL; i++) { |
14874 |
✓✓✓✓ |
2298 |
if (len == (int) strlen(reserved[i]) && strncmp(s, reserved[i], len) == 0) |
14875 |
30 |
return i + 1; |
|
14876 |
} |
||
14877 |
58 |
return 0; |
|
14878 |
} |
||
14879 |
|||
14880 |
107 |
static int getident(struct pstate *p) { |
|
14881 |
✓✓✓✓ |
107 |
while (mjs_is_ident(p->pos[0]) || mjs_is_digit(p->pos[0])) p->pos++; |
14882 |
107 |
p->tok.len = p->pos - p->tok.ptr; |
|
14883 |
107 |
p->pos--; |
|
14884 |
107 |
return TOK_IDENT; |
|
14885 |
} |
||
14886 |
|||
14887 |
28 |
static int getstr(struct pstate *p) { |
|
14888 |
28 |
int quote = *p->pos++; |
|
14889 |
28 |
p->tok.ptr++; |
|
14890 |
✓✓✓✓ |
77 |
while (p->pos[0] != '\0' && p->pos[0] != quote) { |
14891 |
✓✓✓✓ ✓✗ |
30 |
if (p->pos[0] == '\\' && p->pos[1] != '\0' && |
14892 |
✓✓ | 18 |
(p->pos[1] == quote || strchr("bfnrtv\\", p->pos[1]) != NULL)) { |
14893 |
3 |
p->pos += 2; |
|
14894 |
} else { |
||
14895 |
18 |
p->pos++; |
|
14896 |
} |
||
14897 |
} |
||
14898 |
28 |
p->tok.len = p->pos - p->tok.ptr; |
|
14899 |
28 |
return TOK_STR; |
|
14900 |
} |
||
14901 |
|||
14902 |
471 |
static void skip_spaces_and_comments(struct pstate *p) { |
|
14903 |
const char *pos; |
||
14904 |
do { |
||
14905 |
471 |
pos = p->pos; |
|
14906 |
✓✓ | 984 |
while (mjs_is_space(p->pos[0])) { |
14907 |
✓✓ | 42 |
if (p->pos[0] == '\n') p->line_no++; |
14908 |
42 |
p->pos++; |
|
14909 |
} |
||
14910 |
✓✓✓✓ |
471 |
if (p->pos[0] == '/' && p->pos[1] == '/') { |
14911 |
✓✓✓✗ |
2 |
while (p->pos[0] != '\0' && p->pos[0] != '\n') p->pos++; |
14912 |
} |
||
14913 |
✓✓✓✓ |
471 |
if (p->pos[0] == '/' && p->pos[1] == '*') { |
14914 |
4 |
p->pos += 2; |
|
14915 |
✓✓ | 10 |
while (p->pos[0] != '\0') { |
14916 |
✗✓ | 2 |
if (p->pos[0] == '\n') p->line_no++; |
14917 |
✗✓✗✗ |
2 |
if (p->pos[0] == '*' && p->pos[1] == '/') { |
14918 |
p->pos += 2; |
||
14919 |
break; |
||
14920 |
} |
||
14921 |
2 |
p->pos++; |
|
14922 |
} |
||
14923 |
} |
||
14924 |
✓✓ | 471 |
} while (pos < p->pos); |
14925 |
427 |
} |
|
14926 |
|||
14927 |
427 |
static int ptranslate(int tok) { |
|
14928 |
#define DT(a, b) ((a) << 8 | (b)) |
||
14929 |
#define TT(a, b, c) ((a) << 16 | (b) << 8 | (c)) |
||
14930 |
#define QT(a, b, c, d) ((a) << 24 | (b) << 16 | (c) << 8 | (d)) |
||
14931 |
/* Map token ID produced by mjs_tok.c to token ID produced by lemon */ |
||
14932 |
/* clang-format off */ |
||
14933 |
✓✓✓✓ ✓✓✓✓ ✓✓✓✓ ✓✓✓✓ ✓✓✓✓ ✓✓✓✓ ✓✓✓✓ ✓✗✓✓ ✓✗✓✓ ✓✓✓✓ ✓✗✓✗ ✗✓✗✗ ✓ |
427 |
switch (tok) { |
14934 |
2 |
case ':': return TOK_COLON; |
|
14935 |
3 |
case ';': return TOK_SEMICOLON; |
|
14936 |
2 |
case ',': return TOK_COMMA; |
|
14937 |
4 |
case '=': return TOK_ASSIGN; |
|
14938 |
10 |
case '{': return TOK_OPEN_CURLY; |
|
14939 |
8 |
case '}': return TOK_CLOSE_CURLY; |
|
14940 |
8 |
case '(': return TOK_OPEN_PAREN; |
|
14941 |
13 |
case ')': return TOK_CLOSE_PAREN; |
|
14942 |
2 |
case '[': return TOK_OPEN_BRACKET; |
|
14943 |
2 |
case ']': return TOK_CLOSE_BRACKET; |
|
14944 |
6 |
case '*': return TOK_MUL; |
|
14945 |
4 |
case '+': return TOK_PLUS; |
|
14946 |
9 |
case '-': return TOK_MINUS; |
|
14947 |
6 |
case '/': return TOK_DIV; |
|
14948 |
2 |
case '%': return TOK_REM; |
|
14949 |
7 |
case '&': return TOK_AND; |
|
14950 |
9 |
case '|': return TOK_OR; |
|
14951 |
4 |
case '^': return TOK_XOR; |
|
14952 |
2 |
case '.': return TOK_DOT; |
|
14953 |
4 |
case '?': return TOK_QUESTION; |
|
14954 |
2 |
case '!': return TOK_NOT; |
|
14955 |
2 |
case '~': return TOK_TILDA; |
|
14956 |
4 |
case '<': return TOK_LT; |
|
14957 |
2 |
case '>': return TOK_GT; |
|
14958 |
1 |
case DT('<','<'): return TOK_LSHIFT; |
|
14959 |
2 |
case DT('>','>'): return TOK_RSHIFT; |
|
14960 |
5 |
case DT('-','-'): return TOK_MINUS_MINUS; |
|
14961 |
3 |
case DT('+','+'): return TOK_PLUS_PLUS; |
|
14962 |
2 |
case DT('+','='): return TOK_PLUS_ASSIGN; |
|
14963 |
case DT('-','='): return TOK_MINUS_ASSIGN; |
||
14964 |
2 |
case DT('*','='): return TOK_MUL_ASSIGN; |
|
14965 |
2 |
case DT('/','='): return TOK_DIV_ASSIGN; |
|
14966 |
2 |
case DT('&','='): return TOK_AND_ASSIGN; |
|
14967 |
case DT('|','='): return TOK_OR_ASSIGN; |
||
14968 |
2 |
case DT('%','='): return TOK_REM_ASSIGN; |
|
14969 |
2 |
case DT('^','='): return TOK_XOR_ASSIGN; |
|
14970 |
2 |
case DT('=','='): return TOK_EQ; |
|
14971 |
2 |
case DT('!','='): return TOK_NE; |
|
14972 |
2 |
case DT('<','='): return TOK_LE; |
|
14973 |
2 |
case DT('>','='): return TOK_GE; |
|
14974 |
2 |
case DT('&','&'): return TOK_LOGICAL_AND; |
|
14975 |
case DT('|','|'): return TOK_LOGICAL_OR; |
||
14976 |
1 |
case TT('=','=','='): return TOK_EQ_EQ; |
|
14977 |
case TT('!','=','='): return TOK_NE_NE; |
||
14978 |
case TT('<','<','='): return TOK_LSHIFT_ASSIGN; |
||
14979 |
2 |
case TT('>','>','='): return TOK_RSHIFT_ASSIGN; |
|
14980 |
case TT('>','>','>'): return TOK_URSHIFT; |
||
14981 |
case QT('>','>','>','='): return TOK_URSHIFT_ASSIGN; |
||
14982 |
} |
||
14983 |
/* clang-format on */ |
||
14984 |
274 |
return tok; |
|
14985 |
} |
||
14986 |
|||
14987 |
427 |
MJS_PRIVATE int pnext(struct pstate *p) { |
|
14988 |
427 |
int tmp, tok = TOK_INVALID; |
|
14989 |
|||
14990 |
427 |
skip_spaces_and_comments(p); |
|
14991 |
427 |
p->tok.ptr = p->pos; |
|
14992 |
427 |
p->tok.len = 1; |
|
14993 |
|||
14994 |
✓✓ | 427 |
if (p->pos[0] == '\0') { |
14995 |
97 |
tok = TOK_EOF; |
|
14996 |
✓✓ | 427 |
} if (mjs_is_digit(p->pos[0])) { |
14997 |
42 |
tok = getnum(p); |
|
14998 |
✓✓✓✓ |
385 |
} else if (p->pos[0] == '\'' || p->pos[0] == '"') { |
14999 |
28 |
tok = getstr(p); |
|
15000 |
✓✓ | 357 |
} else if (mjs_is_ident(p->pos[0])) { |
15001 |
107 |
tok = getident(p); |
|
15002 |
/* |
||
15003 |
* NOTE: getident() has side effects on `p`, and `is_reserved_word_token()` |
||
15004 |
* relies on them. Since in C the order of evaluation of the operands is |
||
15005 |
* undefined, `is_reserved_word_token()` should be called in a separate |
||
15006 |
* statement. |
||
15007 |
*/ |
||
15008 |
107 |
tok += is_reserved_word_token(p->tok.ptr, p->tok.len); |
|
15009 |
✓✓ | 250 |
} else if (strchr(",.:;{}[]()?", p->pos[0]) != NULL) { |
15010 |
153 |
tok = p->pos[0]; |
|
15011 |
✓✗✓✓ |
97 |
} else if ((tmp = longtok3(p, '<', '<', '=')) != TOK_EOF || |
15012 |
✓✗ | 95 |
(tmp = longtok3(p, '>', '>', '=')) != TOK_EOF || |
15013 |
✓✗ | 95 |
(tmp = longtok4(p, '>', '>', '>', '=')) != TOK_EOF || |
15014 |
✓✓ | 95 |
(tmp = longtok3(p, '>', '>', '>')) != TOK_EOF || |
15015 |
✓✗ | 94 |
(tmp = longtok3(p, '=', '=', '=')) != TOK_EOF || |
15016 |
✓✓ | 94 |
(tmp = longtok3(p, '!', '=', '=')) != TOK_EOF || |
15017 |
✓✓ | 83 |
(tmp = longtok(p, "&", "&=")) != TOK_EOF || |
15018 |
✓✓ | 74 |
(tmp = longtok(p, "|", "|=")) != TOK_EOF || |
15019 |
✓✓ | 67 |
(tmp = longtok(p, "<", "<=")) != TOK_EOF || |
15020 |
✓✓ | 61 |
(tmp = longtok(p, ">", ">=")) != TOK_EOF || |
15021 |
✓✓ | 47 |
(tmp = longtok(p, "-", "-=")) != TOK_EOF || |
15022 |
(tmp = longtok(p, "+", "+=")) != TOK_EOF) { |
||
15023 |
59 |
tok = tmp; |
|
15024 |
✓✗ | 38 |
} else if ((tmp = longtok(p, "^~+-%/*<>=!|&", "=")) != TOK_EOF) { |
15025 |
38 |
tok = tmp; |
|
15026 |
} |
||
15027 |
✓✓ | 427 |
if (p->pos[0] != '\0') p->pos++; |
15028 |
✗✓ | 427 |
LOG(LL_VERBOSE_DEBUG, (" --> %d [%.*s]", tok, p->tok.len, p->tok.ptr)); |
15029 |
427 |
p->prev_tok = p->tok.tok; |
|
15030 |
427 |
p->tok.tok = ptranslate(tok); |
|
15031 |
427 |
return p->tok.tok; |
|
15032 |
} |
||
15033 |
#ifdef MJS_MODULE_LINES |
||
15034 |
#line 1 "mjs/src/mjs_util.c" |
||
15035 |
#endif |
||
15036 |
/* |
||
15037 |
* Copyright (c) 2017 Cesanta Software Limited |
||
15038 |
* All rights reserved |
||
15039 |
*/ |
||
15040 |
|||
15041 |
/* Amalgamated: #include "common/cs_varint.h" */ |
||
15042 |
/* Amalgamated: #include "frozen.h" */ |
||
15043 |
/* Amalgamated: #include "mjs/src/mjs_array.h" */ |
||
15044 |
/* Amalgamated: #include "mjs/src/mjs_bcode.h" */ |
||
15045 |
/* Amalgamated: #include "mjs/src/mjs_core.h" */ |
||
15046 |
/* Amalgamated: #include "mjs/src/mjs_internal.h" */ |
||
15047 |
/* Amalgamated: #include "mjs/src/mjs_object.h" */ |
||
15048 |
/* Amalgamated: #include "mjs/src/mjs_primitive.h" */ |
||
15049 |
/* Amalgamated: #include "mjs/src/mjs_string.h" */ |
||
15050 |
/* Amalgamated: #include "mjs/src/mjs_util.h" */ |
||
15051 |
/* Amalgamated: #include "mjs/src/mjs_tok.h" */ |
||
15052 |
|||
15053 |
1 |
const char *mjs_typeof(mjs_val_t v) { |
|
15054 |
1 |
return mjs_stringify_type(mjs_get_type(v)); |
|
15055 |
} |
||
15056 |
|||
15057 |
1 |
MJS_PRIVATE const char *mjs_stringify_type(enum mjs_type t) { |
|
15058 |
✗✗✓✗ ✗✗✗✗ ✗✗ |
1 |
switch (t) { |
15059 |
case MJS_TYPE_NUMBER: |
||
15060 |
return "number"; |
||
15061 |
case MJS_TYPE_BOOLEAN: |
||
15062 |
return "boolean"; |
||
15063 |
case MJS_TYPE_STRING: |
||
15064 |
1 |
return "string"; |
|
15065 |
case MJS_TYPE_OBJECT_ARRAY: |
||
15066 |
return "array"; |
||
15067 |
case MJS_TYPE_OBJECT_GENERIC: |
||
15068 |
return "object"; |
||
15069 |
case MJS_TYPE_FOREIGN: |
||
15070 |
return "foreign_ptr"; |
||
15071 |
case MJS_TYPE_OBJECT_FUNCTION: |
||
15072 |
return "function"; |
||
15073 |
case MJS_TYPE_NULL: |
||
15074 |
return "null"; |
||
15075 |
case MJS_TYPE_UNDEFINED: |
||
15076 |
return "undefined"; |
||
15077 |
default: |
||
15078 |
return "???"; |
||
15079 |
} |
||
15080 |
} |
||
15081 |
|||
15082 |
66 |
void mjs_jprintf(mjs_val_t v, struct mjs *mjs, struct json_out *out) { |
|
15083 |
✓✓ | 66 |
if (mjs_is_number(v)) { |
15084 |
3 |
double iv, d = mjs_get_double(mjs, v); |
|
15085 |
✓✓ | 3 |
if (modf(d, &iv) == 0) { |
15086 |
1 |
json_printf(out, "%" INT64_FMT, (int64_t) d); |
|
15087 |
} else { |
||
15088 |
2 |
json_printf(out, "%f", mjs_get_double(mjs, v)); |
|
15089 |
} |
||
15090 |
✓✓ | 63 |
} else if (mjs_is_boolean(v)) { |
15091 |
✓✓ | 2 |
json_printf(out, "%s", mjs_get_bool(mjs, v) ? "true" : "false"); |
15092 |
✓✓ | 61 |
} else if (mjs_is_string(v)) { |
15093 |
size_t i, size; |
||
15094 |
5 |
const char *s = mjs_get_string(mjs, &v, &size); |
|
15095 |
✓✓ | 13 |
for (i = 0; i < size; i++) { |
15096 |
8 |
int ch = ((unsigned char *) s)[i]; |
|
15097 |
✓✓ | 8 |
if (isprint(ch)) { |
15098 |
7 |
json_printf(out, "%c", ch); |
|
15099 |
} else { |
||
15100 |
1 |
json_printf(out, "%s%02x", "\\x", ch); |
|
15101 |
} |
||
15102 |
} |
||
15103 |
✗✓ | 56 |
} else if (mjs_is_array(v)) { |
15104 |
json_printf(out, "%s", "<array>"); |
||
15105 |
✗✓ | 56 |
} else if (mjs_is_object(v)) { |
15106 |
json_printf(out, "%s", "<object>"); |
||
15107 |
✗✓ | 56 |
} else if (mjs_is_foreign(v)) { |
15108 |
json_printf(out, "%s%lx%s", "<foreign_ptr@", |
||
15109 |
(unsigned long) (uintptr_t) mjs_get_ptr(mjs, v), ">"); |
||
15110 |
✗✓ | 56 |
} else if (mjs_is_function(v)) { |
15111 |
json_printf(out, "%s%d%s", "<function@", (int) mjs_get_func_addr(v), ">"); |
||
15112 |
✓✓ | 56 |
} else if (mjs_is_null(v)) { |
15113 |
1 |
json_printf(out, "%s", "null"); |
|
15114 |
✓✗ | 55 |
} else if (mjs_is_undefined(v)) { |
15115 |
55 |
json_printf(out, "%s", "undefined"); |
|
15116 |
} else { |
||
15117 |
json_printf(out, "%s%" INT64_FMT "%s", "<???", (int64_t) v, ">"); |
||
15118 |
} |
||
15119 |
66 |
} |
|
15120 |
|||
15121 |
void mjs_sprintf(mjs_val_t v, struct mjs *mjs, char *buf, size_t n) { |
||
15122 |
struct json_out out = JSON_OUT_BUF(buf, n); |
||
15123 |
mjs_jprintf(v, mjs, &out); |
||
15124 |
} |
||
15125 |
|||
15126 |
66 |
void mjs_fprintf(mjs_val_t v, struct mjs *mjs, FILE *fp) { |
|
15127 |
66 |
struct json_out out = JSON_OUT_FILE(fp); |
|
15128 |
66 |
mjs_jprintf(v, mjs, &out); |
|
15129 |
66 |
} |
|
15130 |
|||
15131 |
#if MJS_ENABLE_DEBUG |
||
15132 |
|||
15133 |
248 |
MJS_PRIVATE const char *opcodetostr(uint8_t opcode) { |
|
15134 |
static const char *names[] = { |
||
15135 |
"NOP", "DROP", "DUP", "SWAP", "JMP", "JMP_TRUE", "JMP_NEUTRAL_TRUE", |
||
15136 |
"JMP_FALSE", "JMP_NEUTRAL_FALSE", "FIND_SCOPE", "PUSH_SCOPE", "PUSH_STR", |
||
15137 |
"PUSH_TRUE", "PUSH_FALSE", "PUSH_INT", "PUSH_DBL", "PUSH_NULL", |
||
15138 |
"PUSH_UNDEF", "PUSH_OBJ", "PUSH_ARRAY", "PUSH_FUNC", "PUSH_THIS", "GET", |
||
15139 |
"CREATE", "EXPR", "APPEND", "SET_ARG", "NEW_SCOPE", "DEL_SCOPE", "CALL", |
||
15140 |
"RETURN", "LOOP", "BREAK", "CONTINUE", "SETRETVAL", "EXIT", "BCODE_HDR", |
||
15141 |
"ARGS", "FOR_IN_NEXT", |
||
15142 |
}; |
||
15143 |
248 |
const char *name = "???"; |
|
15144 |
assert(ARRAY_SIZE(names) == OP_MAX); |
||
15145 |
✓✗ | 248 |
if (opcode < ARRAY_SIZE(names)) name = names[opcode]; |
15146 |
248 |
return name; |
|
15147 |
} |
||
15148 |
|||
15149 |
248 |
MJS_PRIVATE size_t mjs_disasm_single(const uint8_t *code, size_t i) { |
|
15150 |
char buf[40]; |
||
15151 |
248 |
size_t start_i = i; |
|
15152 |
size_t llen; |
||
15153 |
uint64_t n; |
||
15154 |
|||
15155 |
248 |
snprintf(buf, sizeof(buf), "\t%-3u %-8s", (unsigned) i, opcodetostr(code[i])); |
|
15156 |
|||
15157 |
✗✓✗✓ ✗✗✓✓ ✓ |
248 |
switch (code[i]) { |
15158 |
case OP_PUSH_FUNC: { |
||
15159 |
cs_varint_decode(&code[i + 1], ~0, &n, &llen); |
||
15160 |
LOG(LL_VERBOSE_DEBUG, ("%s %04u", buf, (unsigned) (i - n))); |
||
15161 |
i += llen; |
||
15162 |
break; |
||
15163 |
} |
||
15164 |
case OP_PUSH_INT: { |
||
15165 |
31 |
cs_varint_decode(&code[i + 1], ~0, &n, &llen); |
|
15166 |
✗✓ | 31 |
LOG(LL_VERBOSE_DEBUG, ("%s\t%lu", buf, (unsigned long) n)); |
15167 |
31 |
i += llen; |
|
15168 |
31 |
break; |
|
15169 |
} |
||
15170 |
case OP_SET_ARG: { |
||
15171 |
size_t llen2; |
||
15172 |
uint64_t arg_no; |
||
15173 |
cs_varint_decode(&code[i + 1], ~0, &arg_no, &llen); |
||
15174 |
cs_varint_decode(&code[i + llen + 1], ~0, &n, &llen2); |
||
15175 |
LOG(LL_VERBOSE_DEBUG, ("%s\t[%.*s] %u", buf, (int) n, |
||
15176 |
code + i + 1 + llen + llen2, (unsigned) arg_no)); |
||
15177 |
i += llen + llen2 + n; |
||
15178 |
break; |
||
15179 |
} |
||
15180 |
case OP_PUSH_STR: |
||
15181 |
case OP_PUSH_DBL: { |
||
15182 |
50 |
cs_varint_decode(&code[i + 1], ~0, &n, &llen); |
|
15183 |
✗✓ | 50 |
LOG(LL_VERBOSE_DEBUG, ("%s\t[%.*s]", buf, (int) n, code + i + 1 + llen)); |
15184 |
50 |
i += llen + n; |
|
15185 |
50 |
break; |
|
15186 |
} |
||
15187 |
case OP_JMP: |
||
15188 |
case OP_JMP_TRUE: |
||
15189 |
case OP_JMP_NEUTRAL_TRUE: |
||
15190 |
case OP_JMP_FALSE: |
||
15191 |
case OP_JMP_NEUTRAL_FALSE: { |
||
15192 |
cs_varint_decode(&code[i + 1], ~0, &n, &llen); |
||
15193 |
LOG(LL_VERBOSE_DEBUG, |
||
15194 |
("%s\t%u", buf, |
||
15195 |
(unsigned) (i + n + llen + |
||
15196 |
1 /* becaue i will be incremented on the usual terms */))); |
||
15197 |
i += llen; |
||
15198 |
break; |
||
15199 |
} |
||
15200 |
case OP_LOOP: { |
||
15201 |
size_t l1, l2; |
||
15202 |
uint64_t n1, n2; |
||
15203 |
cs_varint_decode(&code[i + 1], ~0, &n1, &l1); |
||
15204 |
cs_varint_decode(&code[i + l1 + 1], ~0, &n2, &l2); |
||
15205 |
LOG(LL_VERBOSE_DEBUG, |
||
15206 |
("%s\tB:%lu C:%lu (%d)", buf, |
||
15207 |
(unsigned long) (i + 1 /* OP_LOOP */ + l1 + n1), |
||
15208 |
(unsigned long) (i + 1 /* OP_LOOP */ + l1 + l2 + n2), (int) i)); |
||
15209 |
i += l1 + l2; |
||
15210 |
break; |
||
15211 |
} |
||
15212 |
case OP_EXPR: { |
||
15213 |
13 |
int op = code[i + 1]; |
|
15214 |
13 |
const char *name = "???"; |
|
15215 |
/* clang-format off */ |
||
15216 |
✗✗✗✗ ✓✗✗✗ ✓✗✗✗ ✓✗✗✗ ✗✗✗✗ ✗✗✗✗ ✗✗✗✓ ✗✗✗✓ ✗✗✗✗ ✗✗✗✗ ✗✗✗✗ |
13 |
switch (op) { |
15217 |
case TOK_DOT: name = "."; break; |
||
15218 |
case TOK_MINUS: name = "-"; break; |
||
15219 |
case TOK_PLUS: name = "+"; break; |
||
15220 |
case TOK_MUL: name = "*"; break; |
||
15221 |
5 |
case TOK_DIV: name = "/"; break; |
|
15222 |
case TOK_REM: name = "%"; break; |
||
15223 |
case TOK_XOR: name = "^"; break; |
||
15224 |
case TOK_AND: name = "&"; break; |
||
15225 |
3 |
case TOK_OR: name = "|"; break; |
|
15226 |
case TOK_LSHIFT: name = "<<"; break; |
||
15227 |
case TOK_RSHIFT: name = ">>"; break; |
||
15228 |
case TOK_URSHIFT: name = ">>>"; break; |
||
15229 |
2 |
case TOK_UNARY_MINUS: name = "- (unary)"; break; |
|
15230 |
case TOK_UNARY_PLUS: name = "+ (unary)"; break; |
||
15231 |
case TOK_NOT: name = "!"; break; |
||
15232 |
case TOK_TILDA: name = "~"; break; |
||
15233 |
case TOK_EQ: name = "=="; break; |
||
15234 |
case TOK_NE: name = "!="; break; |
||
15235 |
case TOK_EQ_EQ: name = "==="; break; |
||
15236 |
case TOK_NE_NE: name = "!=="; break; |
||
15237 |
case TOK_LT: name = "<"; break; |
||
15238 |
case TOK_GT: name = ">"; break; |
||
15239 |
case TOK_LE: name = "<="; break; |
||
15240 |
case TOK_GE: name = ">="; break; |
||
15241 |
case TOK_ASSIGN: name = "="; break; |
||
15242 |
case TOK_POSTFIX_PLUS: name = "++ (postfix)"; break; |
||
15243 |
case TOK_POSTFIX_MINUS: name = "-- (postfix)"; break; |
||
15244 |
2 |
case TOK_MINUS_MINUS: name = "--"; break; |
|
15245 |
case TOK_PLUS_PLUS: name = "++"; break; |
||
15246 |
case TOK_LOGICAL_AND: name = "&&"; break; |
||
15247 |
case TOK_LOGICAL_OR: name = "||"; break; |
||
15248 |
1 |
case TOK_KEYWORD_TYPEOF: name = "typeof"; break; |
|
15249 |
case TOK_PLUS_ASSIGN: name = "+="; break; |
||
15250 |
case TOK_MINUS_ASSIGN: name = "-="; break; |
||
15251 |
case TOK_MUL_ASSIGN: name = "*="; break; |
||
15252 |
case TOK_DIV_ASSIGN: name = "/="; break; |
||
15253 |
case TOK_REM_ASSIGN: name = "%="; break; |
||
15254 |
case TOK_XOR_ASSIGN: name = "^="; break; |
||
15255 |
case TOK_AND_ASSIGN: name = "&="; break; |
||
15256 |
case TOK_OR_ASSIGN: name = "|="; break; |
||
15257 |
case TOK_LSHIFT_ASSIGN: name = "<<="; break; |
||
15258 |
case TOK_RSHIFT_ASSIGN: name = ">>="; break; |
||
15259 |
case TOK_URSHIFT_ASSIGN: name = ">>>="; break; |
||
15260 |
} |
||
15261 |
/* clang-format on */ |
||
15262 |
✗✓ | 13 |
LOG(LL_VERBOSE_DEBUG, ("%s\t%s", buf, name)); |
15263 |
13 |
i++; |
|
15264 |
13 |
break; |
|
15265 |
} |
||
15266 |
case OP_BCODE_HEADER: { |
||
15267 |
66 |
size_t start = 0; |
|
15268 |
66 |
mjs_header_item_t map_offset = 0, total_size = 0; |
|
15269 |
66 |
start = i; |
|
15270 |
66 |
memcpy(&total_size, &code[i + 1], sizeof(total_size)); |
|
15271 |
66 |
memcpy(&map_offset, |
|
15272 |
66 |
&code[i + 1 + MJS_HDR_ITEM_MAP_OFFSET * sizeof(total_size)], |
|
15273 |
sizeof(map_offset)); |
||
15274 |
66 |
i += sizeof(mjs_header_item_t) * MJS_HDR_ITEMS_CNT; |
|
15275 |
✗✓ | 66 |
LOG(LL_VERBOSE_DEBUG, ("%s\t[%s] end:%lu map_offset: %lu", buf, |
15276 |
&code[i + 1], (unsigned long) start + total_size, |
||
15277 |
(unsigned long) start + map_offset)); |
||
15278 |
66 |
i += strlen((char *) (code + i + 1)) + 1; |
|
15279 |
66 |
break; |
|
15280 |
} |
||
15281 |
default: |
||
15282 |
✗✓ | 88 |
LOG(LL_VERBOSE_DEBUG, ("%s", buf)); |
15283 |
88 |
break; |
|
15284 |
} |
||
15285 |
248 |
return i - start_i; |
|
15286 |
} |
||
15287 |
|||
15288 |
void mjs_disasm(const uint8_t *code, size_t len) { |
||
15289 |
size_t i, start = 0; |
||
15290 |
mjs_header_item_t map_offset = 0, total_size = 0; |
||
15291 |
|||
15292 |
for (i = 0; i < len; i++) { |
||
15293 |
size_t delta = mjs_disasm_single(code, i); |
||
15294 |
if (code[i] == OP_BCODE_HEADER) { |
||
15295 |
start = i; |
||
15296 |
memcpy(&total_size, &code[i + 1], sizeof(total_size)); |
||
15297 |
memcpy(&map_offset, |
||
15298 |
&code[i + 1 + MJS_HDR_ITEM_MAP_OFFSET * sizeof(total_size)], |
||
15299 |
sizeof(map_offset)); |
||
15300 |
} |
||
15301 |
|||
15302 |
i += delta; |
||
15303 |
|||
15304 |
if (map_offset > 0 && i == start + map_offset) { |
||
15305 |
i = start + total_size - 1; |
||
15306 |
continue; |
||
15307 |
} |
||
15308 |
} |
||
15309 |
} |
||
15310 |
|||
15311 |
static void mjs_dump_obj_stack(const char *name, const struct mbuf *m, |
||
15312 |
struct mjs *mjs) { |
||
15313 |
char buf[50]; |
||
15314 |
size_t i, n; |
||
15315 |
n = mjs_stack_size(m); |
||
15316 |
LOG(LL_VERBOSE_DEBUG, ("%12s (%d elems): ", name, (int) n)); |
||
15317 |
for (i = 0; i < n; i++) { |
||
15318 |
mjs_sprintf(((mjs_val_t *) m->buf)[i], mjs, buf, sizeof(buf)); |
||
15319 |
LOG(LL_VERBOSE_DEBUG, ("%34s", buf)); |
||
15320 |
} |
||
15321 |
} |
||
15322 |
|||
15323 |
void mjs_dump(struct mjs *mjs, int do_disasm) { |
||
15324 |
LOG(LL_VERBOSE_DEBUG, ("------- MJS VM DUMP BEGIN")); |
||
15325 |
mjs_dump_obj_stack("DATA_STACK", &mjs->stack, mjs); |
||
15326 |
mjs_dump_obj_stack("CALL_STACK", &mjs->call_stack, mjs); |
||
15327 |
mjs_dump_obj_stack("SCOPES", &mjs->scopes, mjs); |
||
15328 |
mjs_dump_obj_stack("LOOP_OFFSETS", &mjs->loop_addresses, mjs); |
||
15329 |
mjs_dump_obj_stack("ARG_STACK", &mjs->arg_stack, mjs); |
||
15330 |
if (do_disasm) { |
||
15331 |
int parts_cnt = mjs_bcode_parts_cnt(mjs); |
||
15332 |
int i; |
||
15333 |
LOG(LL_VERBOSE_DEBUG, ("%23s", "CODE:")); |
||
15334 |
for (i = 0; i < parts_cnt; i++) { |
||
15335 |
struct mjs_bcode_part *bp = mjs_bcode_part_get(mjs, i); |
||
15336 |
mjs_disasm((uint8_t *) bp->data.p, bp->data.len); |
||
15337 |
} |
||
15338 |
} |
||
15339 |
LOG(LL_VERBOSE_DEBUG, ("------- MJS VM DUMP END")); |
||
15340 |
} |
||
15341 |
|||
15342 |
MJS_PRIVATE int mjs_check_arg(struct mjs *mjs, int arg_num, |
||
15343 |
const char *arg_name, enum mjs_type expected_type, |
||
15344 |
mjs_val_t *parg) { |
||
15345 |
mjs_val_t arg = MJS_UNDEFINED; |
||
15346 |
enum mjs_type actual_type; |
||
15347 |
|||
15348 |
if (arg_num >= 0) { |
||
15349 |
int nargs = mjs_nargs(mjs); |
||
15350 |
if (nargs < arg_num + 1) { |
||
15351 |
mjs_prepend_errorf(mjs, MJS_TYPE_ERROR, "missing argument %s", arg_name); |
||
15352 |
return 0; |
||
15353 |
} |
||
15354 |
|||
15355 |
arg = mjs_arg(mjs, arg_num); |
||
15356 |
} else { |
||
15357 |
/* use `this` */ |
||
15358 |
arg = mjs->vals.this_obj; |
||
15359 |
} |
||
15360 |
|||
15361 |
actual_type = mjs_get_type(arg); |
||
15362 |
if (actual_type != expected_type) { |
||
15363 |
mjs_prepend_errorf(mjs, MJS_TYPE_ERROR, "%s should be a %s, %s given", |
||
15364 |
arg_name, mjs_stringify_type(expected_type), |
||
15365 |
mjs_stringify_type(actual_type)); |
||
15366 |
return 0; |
||
15367 |
} |
||
15368 |
|||
15369 |
if (parg != NULL) { |
||
15370 |
*parg = arg; |
||
15371 |
} |
||
15372 |
|||
15373 |
return 1; |
||
15374 |
} |
||
15375 |
|||
15376 |
MJS_PRIVATE int mjs_normalize_idx(int idx, int size) { |
||
15377 |
if (idx < 0) { |
||
15378 |
idx = size + idx; |
||
15379 |
if (idx < 0) { |
||
15380 |
idx = 0; |
||
15381 |
} |
||
15382 |
} |
||
15383 |
if (idx > size) { |
||
15384 |
idx = size; |
||
15385 |
} |
||
15386 |
return idx; |
||
15387 |
} |
||
15388 |
|||
15389 |
50 |
MJS_PRIVATE const char *mjs_get_bcode_filename(struct mjs *mjs, |
|
15390 |
struct mjs_bcode_part *bp) { |
||
15391 |
(void) mjs; |
||
15392 |
50 |
return bp->data.p + 1 /* OP_BCODE_HEADER */ + |
|
15393 |
sizeof(mjs_header_item_t) * MJS_HDR_ITEMS_CNT; |
||
15394 |
} |
||
15395 |
|||
15396 |
50 |
const char *mjs_get_bcode_filename_by_offset(struct mjs *mjs, int offset) { |
|
15397 |
50 |
const char *ret = NULL; |
|
15398 |
50 |
struct mjs_bcode_part *bp = mjs_bcode_part_get_by_offset(mjs, offset); |
|
15399 |
✓✗ | 50 |
if (bp != NULL) { |
15400 |
50 |
ret = mjs_get_bcode_filename(mjs, bp); |
|
15401 |
} |
||
15402 |
50 |
return ret; |
|
15403 |
} |
||
15404 |
|||
15405 |
50 |
int mjs_get_lineno_by_offset(struct mjs *mjs, int offset) { |
|
15406 |
size_t llen; |
||
15407 |
uint64_t map_len; |
||
15408 |
50 |
int prev_line_no, ret = 1; |
|
15409 |
50 |
struct mjs_bcode_part *bp = mjs_bcode_part_get_by_offset(mjs, offset); |
|
15410 |
uint8_t *p, *pe; |
||
15411 |
✓✗ | 50 |
if (bp != NULL) { |
15412 |
mjs_header_item_t map_offset, bcode_offset; |
||
15413 |
50 |
memcpy(&map_offset, bp->data.p + 1 /* OP_BCODE_HEADER */ + |
|
15414 |
sizeof(mjs_header_item_t) * MJS_HDR_ITEM_MAP_OFFSET, |
||
15415 |
sizeof(map_offset)); |
||
15416 |
|||
15417 |
50 |
memcpy(&bcode_offset, |
|
15418 |
50 |
bp->data.p + 1 /* OP_BCODE_HEADER */ + |
|
15419 |
sizeof(mjs_header_item_t) * MJS_HDR_ITEM_BCODE_OFFSET, |
||
15420 |
sizeof(bcode_offset)); |
||
15421 |
|||
15422 |
50 |
offset -= (1 /* OP_BCODE_HEADER */ + bcode_offset) + bp->start_idx; |
|
15423 |
|||
15424 |
/* get pointer to the length of the map followed by the map itself */ |
||
15425 |
50 |
p = (uint8_t *) bp->data.p + 1 /* OP_BCODE_HEADER */ + map_offset; |
|
15426 |
|||
15427 |
50 |
cs_varint_decode(p, ~0, &map_len, &llen); |
|
15428 |
50 |
p += llen; |
|
15429 |
50 |
pe = p + map_len; |
|
15430 |
|||
15431 |
50 |
prev_line_no = 1; |
|
15432 |
✓✓ | 101 |
while (p < pe) { |
15433 |
uint64_t cur_offset, line_no; |
||
15434 |
6 |
cs_varint_decode(p, ~0, &cur_offset, &llen); |
|
15435 |
6 |
p += llen; |
|
15436 |
6 |
cs_varint_decode(p, ~0, &line_no, &llen); |
|
15437 |
6 |
p += llen; |
|
15438 |
|||
15439 |
✓✓ | 6 |
if (cur_offset >= (uint64_t) offset) { |
15440 |
5 |
ret = prev_line_no; |
|
15441 |
5 |
break; |
|
15442 |
} |
||
15443 |
1 |
prev_line_no = line_no; |
|
15444 |
} |
||
15445 |
} |
||
15446 |
50 |
return ret; |
|
15447 |
} |
||
15448 |
|||
15449 |
int mjs_get_offset_by_call_frame_num(struct mjs *mjs, int cf_num) { |
||
15450 |
int ret = -1; |
||
15451 |
if (cf_num == 0) { |
||
15452 |
/* Return current bcode offset */ |
||
15453 |
ret = mjs->cur_bcode_offset; |
||
15454 |
} else if (cf_num > 0 && |
||
15455 |
mjs->call_stack.len >= |
||
15456 |
sizeof(mjs_val_t) * CALL_STACK_FRAME_ITEMS_CNT * cf_num) { |
||
15457 |
/* Get offset from the call_stack */ |
||
15458 |
int pos = CALL_STACK_FRAME_ITEM_RETURN_ADDR + |
||
15459 |
CALL_STACK_FRAME_ITEMS_CNT * (cf_num - 1); |
||
15460 |
mjs_val_t val = *vptr(&mjs->call_stack, -1 - pos); |
||
15461 |
ret = mjs_get_int(mjs, val); |
||
15462 |
} |
||
15463 |
return ret; |
||
15464 |
} |
||
15465 |
|||
15466 |
#endif |
||
15467 |
#endif /* MJS_EXPORT_INTERNAL_HEADERS */ |
| Generated by: GCOVR (Version 4.1) |